uint32_t AudioStreamOutALSA::latency() const { int err; int t; snd_pcm_status_t *status; snd_pcm_status_alloca(&status); if(mHandle->handle == NULL) { LOGV("handle is null, error !"); return 0; } if ((err = snd_pcm_status(mHandle->handle, status)) < 0) { LOGV("stream status error :%s\n", snd_strerror(err)); return USEC_TO_MSEC (mHandle->latency); } t = snd_pcm_status_get_delay(status); LOGV("snd_pcm_status_get_delay = %d", t); LOGV("AudioStreamOutALSA::latency = %d, sampleRate = %d", (t * 1000) / sampleRate(), sampleRate()); return (t * 1000) / sampleRate(); }
/* stolen from devposs */ static uint32_t gettimer(void) { long tmp=playpos; int odelay; if (busy++) { odelay=kernlen; } else { int err; #ifdef ALSA_DEBUG fprintf(stderr, "ALSA snd_pcm_status(alsa_pcm, alsa_pcm_status) = "); #endif if ((err=snd_pcm_status(alsa_pcm, alsa_pcm_status))<0) { #ifdef ALSA_DEBUG fprintf(stderr, "failed: %s\n", snd_strerror(-err)); #endif fprintf(stderr, "ALSA: snd_pcm_status() failed: %s\n", snd_strerror(-err)); odelay=kernlen; } else { #ifdef ALSA_DEBUG fprintf(stderr, "ok\n"); fprintf(stderr, "snd_pcm_status_get_delay(alsa_pcm_status) = "); #endif odelay=snd_pcm_status_get_delay(alsa_pcm_status); #ifdef ALSA_DEBUG fprintf(stderr, "%i\n", odelay); #endif if (odelay<0) /* we ignore buffer-underruns */ odelay=0; else if (odelay==0) { /* ALSA sometimes (atlast on Stians Ubuntu laptop) gives odelay==0 always */ odelay = snd_pcm_status_get_avail_max(alsa_pcm_status) - snd_pcm_status_get_avail(alsa_pcm_status); if (odelay<0) odelay=0; } odelay<<=(bit16+stereo); if (odelay>kernlen) { odelay=kernlen; } else if ((odelay<kernlen)) { kernlen=odelay; kernpos=(cachepos-kernlen+buflen)%buflen; } } } tmp-=odelay; busy--; return imuldiv(tmp, 65536>>(stereo+bit16), plrRate); }
/* more or less stolen from devposs */ static int getplaypos(void) { int retval; if (busy++) { } else { snd_pcm_sframes_t tmp; int err; #ifdef ALSA_DEBUG fprintf(stderr, "ALSA snd_pcm_status(alsa_pcm, alsa_pcm_status) = "); #endif if ((err=snd_pcm_status(alsa_pcm, alsa_pcm_status))<0) { #ifdef ALSA_DEBUG fprintf(stderr, "failed: %s\n", snd_strerror(-err)); #endif fprintf(stderr, "ALSA: snd_pcm_status() failed: %s\n", snd_strerror(-err)); } else { #ifdef ALSA_DEBUG fprintf(stderr, "ok\n"); fprintf(stderr, "ALSA snd_pcm_status_get_delay(alsa_pcm_status = "); #endif tmp=snd_pcm_status_get_delay(alsa_pcm_status); #ifdef ALSA_DEBUG fprintf(stderr, "%ld\n", tmp); #endif tmp<<=(bit16+stereo); if (tmp<0) /* we ignore buffer-underruns */ tmp=0; else if (tmp==0) { /* ALSA sometimes (atlast on Stians Ubuntu laptop) gives odelay==0 always */ tmp = snd_pcm_status_get_avail_max(alsa_pcm_status) - snd_pcm_status_get_avail(alsa_pcm_status); if (tmp<0) tmp=0; } if (tmp>kernlen) { } else { kernlen=tmp; } kernpos=(cachepos-kernlen+buflen)%buflen; } } retval=kernpos; busy--; return retval; }
/*---------------------------------------------------------------------- | AlsaOutput_GetStatus +---------------------------------------------------------------------*/ BLT_METHOD AlsaOutput_GetStatus(BLT_OutputNode* _self, BLT_OutputNodeStatus* status) { AlsaOutput* self = ATX_SELF(AlsaOutput, BLT_OutputNode); snd_pcm_status_t* pcm_status; snd_pcm_sframes_t delay = 0; int io_result; /* default values */ status->media_time.seconds = 0; status->media_time.nanoseconds = 0; status->flags = 0; /* get the driver status */ snd_pcm_status_alloca_no_assert(&pcm_status); io_result = snd_pcm_status(self->device_handle, pcm_status); if (io_result != 0) { return BLT_FAILURE; } delay = snd_pcm_status_get_delay(pcm_status); if (delay == 0) { /* workaround buggy alsa drivers */ io_result = snd_pcm_delay(self->device_handle, &delay); if (io_result != 0) { return BLT_FAILURE; } } if (delay > 0 && self->media_type.sample_rate) { ATX_UInt64 media_time_samples = (self->next_media_time * (ATX_UInt64)self->media_type.sample_rate)/ (ATX_UInt64)1000000000; ATX_UInt64 media_time_ns; if (delay <= (snd_pcm_sframes_t)media_time_samples) { media_time_samples -= delay; } else { media_time_samples = 0; } media_time_ns = (media_time_samples*(ATX_UInt64)1000000000)/self->media_type.sample_rate; status->media_time = BLT_TimeStamp_FromNanos(media_time_ns); } else { status->media_time = BLT_TimeStamp_FromNanos(self->next_media_time); } /* return the computed media time */ ATX_LOG_FINEST_3("delay = %lld samples, input port time = %lld, media time = %lld", (ATX_UInt64)delay, self->next_media_time, BLT_TimeStamp_ToNanos(status->media_time)); return BLT_SUCCESS; }
int main(int argc, char *argv[]) { const char *dev; int r, cap, count = 0; snd_pcm_hw_params_t *hwparams; snd_pcm_sw_params_t *swparams; snd_pcm_status_t *status; snd_pcm_t *pcm; unsigned rate = 44100; unsigned periods = 2; snd_pcm_uframes_t boundary, buffer_size = 44100/10; /* 100s */ int dir = 1; struct timespec start, last_timestamp = { 0, 0 }; uint64_t start_us, last_us = 0; snd_pcm_sframes_t last_avail = 0, last_delay = 0; struct pollfd *pollfds; int n_pollfd; int64_t sample_count = 0; struct sched_param sp; r = -1; #ifdef _POSIX_PRIORITY_SCHEDULING sp.sched_priority = 5; r = pthread_setschedparam(pthread_self(), SCHED_RR, &sp); #endif if (r) printf("Could not get RT prio. :(\n"); snd_pcm_hw_params_alloca(&hwparams); snd_pcm_sw_params_alloca(&swparams); snd_pcm_status_alloca(&status); r = clock_gettime(CLOCK_MONOTONIC, &start); assert(r == 0); start_us = timespec_us(&start); dev = argc > 1 ? argv[1] : "front:AudioPCI"; cap = argc > 2 ? atoi(argv[2]) : 0; if (cap == 0) r = snd_pcm_open(&pcm, dev, SND_PCM_STREAM_PLAYBACK, 0); else r = snd_pcm_open(&pcm, dev, SND_PCM_STREAM_CAPTURE, 0); assert(r == 0); r = snd_pcm_hw_params_any(pcm, hwparams); assert(r == 0); r = snd_pcm_hw_params_set_rate_resample(pcm, hwparams, 0); assert(r == 0); r = snd_pcm_hw_params_set_access(pcm, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED); assert(r == 0); r = snd_pcm_hw_params_set_format(pcm, hwparams, SND_PCM_FORMAT_S16_LE); assert(r == 0); r = snd_pcm_hw_params_set_rate_near(pcm, hwparams, &rate, NULL); assert(r == 0); r = snd_pcm_hw_params_set_channels(pcm, hwparams, 2); assert(r == 0); r = snd_pcm_hw_params_set_periods_integer(pcm, hwparams); assert(r == 0); r = snd_pcm_hw_params_set_periods_near(pcm, hwparams, &periods, &dir); assert(r == 0); r = snd_pcm_hw_params_set_buffer_size_near(pcm, hwparams, &buffer_size); assert(r == 0); r = snd_pcm_hw_params(pcm, hwparams); assert(r == 0); r = snd_pcm_hw_params_current(pcm, hwparams); assert(r == 0); r = snd_pcm_sw_params_current(pcm, swparams); assert(r == 0); if (cap == 0) r = snd_pcm_sw_params_set_avail_min(pcm, swparams, 1); else r = snd_pcm_sw_params_set_avail_min(pcm, swparams, 0); assert(r == 0); r = snd_pcm_sw_params_set_period_event(pcm, swparams, 0); assert(r == 0); r = snd_pcm_hw_params_get_buffer_size(hwparams, &buffer_size); assert(r == 0); r = snd_pcm_sw_params_set_start_threshold(pcm, swparams, buffer_size); assert(r == 0); r = snd_pcm_sw_params_get_boundary(swparams, &boundary); assert(r == 0); r = snd_pcm_sw_params_set_stop_threshold(pcm, swparams, boundary); assert(r == 0); r = snd_pcm_sw_params_set_tstamp_mode(pcm, swparams, SND_PCM_TSTAMP_ENABLE); assert(r == 0); r = snd_pcm_sw_params(pcm, swparams); assert(r == 0); r = snd_pcm_prepare(pcm); assert(r == 0); r = snd_pcm_sw_params_current(pcm, swparams); assert(r == 0); /* assert(snd_pcm_hw_params_is_monotonic(hwparams) > 0); */ n_pollfd = snd_pcm_poll_descriptors_count(pcm); assert(n_pollfd > 0); pollfds = malloc(sizeof(struct pollfd) * n_pollfd); assert(pollfds); r = snd_pcm_poll_descriptors(pcm, pollfds, n_pollfd); assert(r == n_pollfd); printf("Starting. Buffer size is %u frames\n", (unsigned int) buffer_size); if (cap) { r = snd_pcm_start(pcm); assert(r == 0); } for (;;) { snd_pcm_sframes_t avail, delay; struct timespec now, timestamp; unsigned short revents; int handled = 0; uint64_t now_us, timestamp_us; snd_pcm_state_t state; unsigned long long pos; r = poll(pollfds, n_pollfd, 0); assert(r >= 0); r = snd_pcm_poll_descriptors_revents(pcm, pollfds, n_pollfd, &revents); assert(r == 0); if (cap == 0) assert((revents & ~POLLOUT) == 0); else assert((revents & ~POLLIN) == 0); avail = snd_pcm_avail(pcm); assert(avail >= 0); r = snd_pcm_status(pcm, status); assert(r == 0); /* This assertion fails from time to time. ALSA seems to be broken */ /* assert(avail == (snd_pcm_sframes_t) snd_pcm_status_get_avail(status)); */ /* printf("%lu %lu\n", (unsigned long) avail, (unsigned long) snd_pcm_status_get_avail(status)); */ snd_pcm_status_get_htstamp(status, ×tamp); delay = snd_pcm_status_get_delay(status); state = snd_pcm_status_get_state(status); r = clock_gettime(CLOCK_MONOTONIC, &now); assert(r == 0); assert(!revents || avail > 0); if ((!cap && avail) || (cap && (unsigned)avail >= buffer_size)) { snd_pcm_sframes_t sframes; static const uint16_t psamples[2] = { 0, 0 }; uint16_t csamples[2]; if (cap == 0) sframes = snd_pcm_writei(pcm, psamples, 1); else sframes = snd_pcm_readi(pcm, csamples, 1); assert(sframes == 1); handled = 1; sample_count++; } if (!handled && memcmp(×tamp, &last_timestamp, sizeof(timestamp)) == 0 && avail == last_avail && delay == last_delay) { /* This is boring */ continue; } now_us = timespec_us(&now); timestamp_us = timespec_us(×tamp); if (cap == 0) pos = (unsigned long long) ((sample_count - handled - delay) * 1000000LU / 44100); else pos = (unsigned long long) ((sample_count - handled + delay) * 1000000LU / 44100); if (count++ % 50 == 0) printf("Elapsed\tCPU\tALSA\tPos\tSamples\tavail\tdelay\trevents\thandled\tstate\n"); printf("%llu\t%llu\t%llu\t%llu\t%llu\t%li\t%li\t%i\t%i\t%i\n", (unsigned long long) (now_us - last_us), (unsigned long long) (now_us - start_us), (unsigned long long) (timestamp_us ? timestamp_us - start_us : 0), pos, (unsigned long long) sample_count, (signed long) avail, (signed long) delay, revents, handled, state); if (cap == 0) /** When this assert is hit, most likely something bad * happened, i.e. the avail jumped suddenly. */ assert((unsigned) avail <= buffer_size); last_avail = avail; last_delay = delay; last_timestamp = timestamp; last_us = now_us; } return 0; }
/* more or less stolen from devposs */ static void flush(void) { int result, n, odelay; int err; if (busy++) { busy--; return; } #ifdef ALSA_DEBUG fprintf(stderr, "ALSA snd_pcm_status(alsa_pcm, alsa_pcm_status) = "); #endif if ((err=snd_pcm_status(alsa_pcm, alsa_pcm_status))<0) { #ifdef ALSA_DEBUG fprintf(stderr, "failed: %s\n", snd_strerror(-err)); #endif fprintf(stderr, "ALSA: snd_pcm_status() failed: %s\n", snd_strerror(-err)); busy--; return; } #ifdef ALSA_DEBUG fprintf(stderr, "ok\n"); fprintf(stderr, "ALSA snd_pcm_status_get_delay(alsa_pcm_status) = "); #endif odelay=snd_pcm_status_get_delay(alsa_pcm_status); #ifdef ALSA_DEBUG fprintf(stderr, "%i\n", odelay); #endif odelay<<=(bit16+stereo); if (odelay<0) /* we ignore buffer-underruns */ odelay=0; else if (odelay==0) { /* ALSA sometimes (atlast on Stians Ubuntu laptop) gives odelay==0 always */ odelay = snd_pcm_status_get_avail_max(alsa_pcm_status) - snd_pcm_status_get_avail(alsa_pcm_status); if (odelay<0) odelay=0; } if (odelay>kernlen) { odelay=kernlen; } else if ((odelay<kernlen)) { kernlen=odelay; kernpos=(cachepos-kernlen+buflen)%buflen; } if (!cachelen) { busy--; return; } if (bufpos<=cachepos) n=buflen-cachepos; else n=bufpos-cachepos; /* TODO, check kernel-size if (n>info.bytes) n=info.bytes; */ if (n<=0) { busy--; return; } #ifdef ALSA_DEBUG fprintf(stderr, "ALSA snd_pcm_writei(alsa_pcm, buffer, %i) = ", n>>(bit16+stereo)); #endif result=snd_pcm_writei(alsa_pcm, playbuf+cachepos, n>>(bit16+stereo)); if (result<0) { #ifdef ALSA_DEBUG fprintf(stderr, "failed: %s\n", snd_strerror(-result)); #endif if (result==-EPIPE) { fprintf(stderr, "ALSA: Machine is too slow, calling snd_pcm_prepare()\n"); fprintf(stderr, "ALSA snd_pcm_prepare(alsa_pcm)"); snd_pcm_prepare(alsa_pcm); /* TODO, can this fail? */ } busy--; return; #ifdef ALSA_DEBUG } else { fprintf(stderr, "ok\n"); #endif } result<<=(bit16+stereo); cachepos=(cachepos+result+buflen)%buflen; playpos+=result; cachelen-=result; kernlen+=result; busy--; }
void *CallbackThread( void *userData ) { PaAlsaStream *stream = (PaAlsaStream*)userData; pthread_cleanup_push( &Stop, stream ); // Execute Stop on exit if( stream->pcm_playback ) snd_pcm_start( stream->pcm_playback ); else if( stream->pcm_capture ) snd_pcm_start( stream->pcm_capture ); while(1) { int frames_avail; int frames_got; PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /* IMPLEMENT ME */ int callbackResult; int framesProcessed; pthread_testcancel(); { /* calculate time info */ snd_timestamp_t capture_timestamp; snd_timestamp_t playback_timestamp; snd_pcm_status_t *capture_status; snd_pcm_status_t *playback_status; snd_pcm_status_alloca( &capture_status ); snd_pcm_status_alloca( &playback_status ); if( stream->pcm_capture ) { snd_pcm_status( stream->pcm_capture, capture_status ); snd_pcm_status_get_tstamp( capture_status, &capture_timestamp ); } if( stream->pcm_playback ) { snd_pcm_status( stream->pcm_playback, playback_status ); snd_pcm_status_get_tstamp( playback_status, &playback_timestamp ); } /* Hmm, we potentially have both a playback and a capture timestamp. * Hopefully they are the same... */ if( stream->pcm_capture && stream->pcm_playback ) { float capture_time = capture_timestamp.tv_sec + ((float)capture_timestamp.tv_usec/1000000); float playback_time= playback_timestamp.tv_sec + ((float)playback_timestamp.tv_usec/1000000); if( fabsf(capture_time-playback_time) > 0.01 ) PA_DEBUG(("Capture time and playback time differ by %f\n", fabsf(capture_time-playback_time))); timeInfo.currentTime = capture_time; } else if( stream->pcm_playback ) { timeInfo.currentTime = playback_timestamp.tv_sec + ((float)playback_timestamp.tv_usec/1000000); } else { timeInfo.currentTime = capture_timestamp.tv_sec + ((float)capture_timestamp.tv_usec/1000000); } if( stream->pcm_capture ) { snd_pcm_sframes_t capture_delay = snd_pcm_status_get_delay( capture_status ); timeInfo.inputBufferAdcTime = timeInfo.currentTime - (float)capture_delay / stream->streamRepresentation.streamInfo.sampleRate; } if( stream->pcm_playback ) { snd_pcm_sframes_t playback_delay = snd_pcm_status_get_delay( playback_status ); timeInfo.outputBufferDacTime = timeInfo.currentTime + (float)playback_delay / stream->streamRepresentation.streamInfo.sampleRate; } } /* IMPLEMENT ME: - handle buffer slips */ /* depending on whether the host buffers are interleaved, non-interleaved or a mixture, you will want to call PaUtil_ProcessInterleavedBuffers(), PaUtil_ProcessNonInterleavedBuffers() or PaUtil_ProcessBuffers() here. */ framesProcessed = frames_avail = wait( stream ); while( frames_avail > 0 ) { //PA_DEBUG(( "%d frames available\n", frames_avail )); /* Now we know the soundcard is ready to produce/receive at least * one period. We just need to get the buffers for the client * to read/write. */ PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, 0 /* @todo pass underflow/overflow flags when necessary */ ); frames_got = setup_buffers( stream, frames_avail ); PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer ); callbackResult = paContinue; /* this calls the callback */ framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult ); PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed ); /* inform ALSA how many frames we wrote */ if( stream->pcm_capture ) snd_pcm_mmap_commit( stream->pcm_capture, stream->capture_offset, frames_got ); if( stream->pcm_playback ) snd_pcm_mmap_commit( stream->pcm_playback, stream->playback_offset, frames_got ); if( callbackResult != paContinue ) break; frames_avail -= frames_got; } /* If you need to byte swap outputBuffer, you can do it here using routines in pa_byteswappers.h */ if( callbackResult != paContinue ) { stream->callback_finished = 1; stream->callbackAbort = (callbackResult == paAbort); pthread_exit( NULL ); } } /* This code is unreachable, but important to include regardless because it * is possibly a macro with a closing brace to match the opening brace in * pthread_cleanup_push() above. The documentation states that they must * always occur in pairs. */ pthread_cleanup_pop( 1 ); }
/***************************************************************************** * ALSAFill: function used to fill the ALSA buffer as much as possible *****************************************************************************/ static void ALSAFill( aout_instance_t * p_aout ) { struct aout_sys_t * p_sys = p_aout->output.p_sys; aout_buffer_t * p_buffer; snd_pcm_status_t * p_status = p_sys->p_status; int i_snd_rc; mtime_t next_date; /* Fill in the buffer until space or audio output buffer shortage */ /* Get the status */ i_snd_rc = snd_pcm_status( p_sys->p_snd_pcm, p_status ); if( i_snd_rc < 0 ) { msg_Err( p_aout, "cannot get device status" ); goto error; } /* Handle buffer underruns and get the status again */ if( snd_pcm_status_get_state( p_status ) == SND_PCM_STATE_XRUN ) { /* Prepare the device */ i_snd_rc = snd_pcm_prepare( p_sys->p_snd_pcm ); if( i_snd_rc ) { msg_Err( p_aout, "cannot recover from buffer underrun" ); goto error; } msg_Dbg( p_aout, "recovered from buffer underrun" ); /* Get the new status */ i_snd_rc = snd_pcm_status( p_sys->p_snd_pcm, p_status ); if( i_snd_rc < 0 ) { msg_Err( p_aout, "cannot get device status after recovery" ); goto error; } /* Underrun, try to recover as quickly as possible */ next_date = mdate(); } else { /* Here the device should be in RUNNING state, p_status is valid. */ snd_pcm_sframes_t delay = snd_pcm_status_get_delay( p_status ); if( delay == 0 ) /* workaround buggy alsa drivers */ if( snd_pcm_delay( p_sys->p_snd_pcm, &delay ) < 0 ) delay = 0; /* FIXME: use a positive minimal delay */ int i_bytes = snd_pcm_frames_to_bytes( p_sys->p_snd_pcm, delay ); next_date = mdate() + ( (mtime_t)i_bytes * 1000000 / p_aout->output.output.i_bytes_per_frame / p_aout->output.output.i_rate * p_aout->output.output.i_frame_length ); #ifdef ALSA_DEBUG snd_pcm_state_t state = snd_pcm_status_get_state( p_status ); if( state != SND_PCM_STATE_RUNNING ) msg_Err( p_aout, "pcm status (%d) != RUNNING", state ); msg_Dbg( p_aout, "Delay is %ld frames (%d bytes)", delay, i_bytes ); msg_Dbg( p_aout, "Bytes per frame: %d", p_aout->output.output.i_bytes_per_frame ); msg_Dbg( p_aout, "Rate: %d", p_aout->output.output.i_rate ); msg_Dbg( p_aout, "Frame length: %d", p_aout->output.output.i_frame_length ); msg_Dbg( p_aout, "Next date is in %d microseconds", (int)(next_date - mdate()) ); #endif } p_buffer = aout_OutputNextBuffer( p_aout, next_date, (p_aout->output.output.i_format == VLC_CODEC_SPDIFL) ); /* Audio output buffer shortage -> stop the fill process and wait */ if( p_buffer == NULL ) goto error; for (;;) { i_snd_rc = snd_pcm_writei( p_sys->p_snd_pcm, p_buffer->p_buffer, p_buffer->i_nb_samples ); if( i_snd_rc != -ESTRPIPE ) break; /* a suspend event occurred * (stream is suspended and waiting for an application recovery) */ msg_Dbg( p_aout, "entering in suspend mode, trying to resume..." ); while( vlc_object_alive (p_aout) && vlc_object_alive (p_aout->p_libvlc) && ( i_snd_rc = snd_pcm_resume( p_sys->p_snd_pcm ) ) == -EAGAIN ) { msleep( 1000000 ); } if( i_snd_rc < 0 ) /* Device does not supprot resuming, restart it */ i_snd_rc = snd_pcm_prepare( p_sys->p_snd_pcm ); } if( i_snd_rc < 0 ) msg_Err( p_aout, "cannot write: %s", snd_strerror( i_snd_rc ) ); aout_BufferFree( p_buffer ); return; error: if( i_snd_rc < 0 ) msg_Err( p_aout, "ALSA error: %s", snd_strerror( i_snd_rc ) ); msleep( p_sys->i_period_time >> 1 ); }
/***************************************************************************** * ALSAFill: function used to fill the ALSA buffer as much as possible *****************************************************************************/ static void ALSAFill( aout_instance_t * p_aout ) { struct aout_sys_t * p_sys = p_aout->output.p_sys; snd_pcm_t *p_pcm = p_sys->p_snd_pcm; snd_pcm_status_t * p_status; int i_snd_rc; mtime_t next_date; int canc = vlc_savecancel(); /* Fill in the buffer until space or audio output buffer shortage */ /* Get the status */ snd_pcm_status_alloca(&p_status); i_snd_rc = snd_pcm_status( p_pcm, p_status ); if( i_snd_rc < 0 ) { msg_Err( p_aout, "cannot get device status" ); goto error; } /* Handle buffer underruns and get the status again */ if( snd_pcm_status_get_state( p_status ) == SND_PCM_STATE_XRUN ) { /* Prepare the device */ i_snd_rc = snd_pcm_prepare( p_pcm ); if( i_snd_rc ) { msg_Err( p_aout, "cannot recover from buffer underrun" ); goto error; } msg_Dbg( p_aout, "recovered from buffer underrun" ); /* Get the new status */ i_snd_rc = snd_pcm_status( p_pcm, p_status ); if( i_snd_rc < 0 ) { msg_Err( p_aout, "cannot get device status after recovery" ); goto error; } /* Underrun, try to recover as quickly as possible */ next_date = mdate(); } else { /* Here the device should be in RUNNING state, p_status is valid. */ snd_pcm_sframes_t delay = snd_pcm_status_get_delay( p_status ); if( delay == 0 ) /* workaround buggy alsa drivers */ if( snd_pcm_delay( p_pcm, &delay ) < 0 ) delay = 0; /* FIXME: use a positive minimal delay */ size_t i_bytes = snd_pcm_frames_to_bytes( p_pcm, delay ); mtime_t delay_us = CLOCK_FREQ * i_bytes / p_aout->output.output.i_bytes_per_frame / p_aout->output.output.i_rate * p_aout->output.output.i_frame_length; #ifdef ALSA_DEBUG snd_pcm_state_t state = snd_pcm_status_get_state( p_status ); if( state != SND_PCM_STATE_RUNNING ) msg_Err( p_aout, "pcm status (%d) != RUNNING", state ); msg_Dbg( p_aout, "Delay is %ld frames (%zu bytes)", delay, i_bytes ); msg_Dbg( p_aout, "Bytes per frame: %d", p_aout->output.output.i_bytes_per_frame ); msg_Dbg( p_aout, "Rate: %d", p_aout->output.output.i_rate ); msg_Dbg( p_aout, "Frame length: %d", p_aout->output.output.i_frame_length ); msg_Dbg( p_aout, "Next date: in %"PRId64" microseconds", delay_us ); #endif next_date = mdate() + delay_us; } block_t *p_buffer = aout_OutputNextBuffer( p_aout, next_date, (p_aout->output.output.i_format == VLC_CODEC_SPDIFL) ); /* Audio output buffer shortage -> stop the fill process and wait */ if( p_buffer == NULL ) goto error; for (;;) { int n = snd_pcm_poll_descriptors_count(p_pcm); struct pollfd ufd[n]; unsigned short revents; snd_pcm_poll_descriptors(p_pcm, ufd, n); do { vlc_restorecancel(canc); poll(ufd, n, -1); canc = vlc_savecancel(); snd_pcm_poll_descriptors_revents(p_pcm, ufd, n, &revents); } while(!revents); if(revents & POLLOUT) { i_snd_rc = snd_pcm_writei( p_pcm, p_buffer->p_buffer, p_buffer->i_nb_samples ); if( i_snd_rc != -ESTRPIPE ) break; } /* a suspend event occurred * (stream is suspended and waiting for an application recovery) */ msg_Dbg( p_aout, "entering in suspend mode, trying to resume..." ); while( ( i_snd_rc = snd_pcm_resume( p_pcm ) ) == -EAGAIN ) { vlc_restorecancel(canc); msleep(CLOCK_FREQ); /* device still suspended, wait... */ canc = vlc_savecancel(); } if( i_snd_rc < 0 ) /* Device does not support resuming, restart it */ i_snd_rc = snd_pcm_prepare( p_pcm ); } if( i_snd_rc < 0 ) msg_Err( p_aout, "cannot write: %s", snd_strerror( i_snd_rc ) ); vlc_restorecancel(canc); block_Release( p_buffer ); return; error: if( i_snd_rc < 0 ) msg_Err( p_aout, "ALSA error: %s", snd_strerror( i_snd_rc ) ); vlc_restorecancel(canc); msleep(p_sys->i_period_time / 2); }