📄 pa_linux_alsa.c
字号:
if( getenv( "PA_ALSA_REALTIME" ) && atoi( getenv( "PA_ALSA_REALTIME" ) ) ) { struct sched_param spm = { 0 }; int maxPri = sched_get_priority_max( SCHED_FIFO ), minPri = sched_get_priority_min( SCHED_FIFO ); spm.sched_priority = (maxPri - minPri) / 2 + minPri; if( pthread_setschedparam( stream->callback_thread, SCHED_FIFO, &spm ) != 0 ) PA_DEBUG(( "Failed bumping priority\n" )); } /*! Wait for stream to be started */ ts.tv_sec = (__time_t) pt + 1; ts.tv_nsec = (long) pt * 1000000000; /* Since we'll be holding a lock on the startMtx (when not waiting on the condition), IsRunning won't be checking * stream state at the same time as the callback thread affects it. We also check IsStreamActive, in the unlikely * case the callback thread exits in the meantime (the stream will be considered inactive after the thread exits) */ UNLESS( !pthread_mutex_lock( &stream->startMtx ), paInternalError ); while( !IsRunning( stream ) && res != ETIMEDOUT && IsStreamActive( s ) ) res = pthread_cond_timedwait( &stream->startCond, &stream->startMtx, &ts ); UNLESS( !pthread_mutex_unlock( &stream->startMtx ), paInternalError ); PA_DEBUG(( "StartStream: Waited for %g seconds for stream to start\n", PaUtil_GetTime() - pt )); if( res == ETIMEDOUT ) { pthread_cancel( stream->callback_thread ); pthread_join( stream->callback_thread, NULL ); PA_ENSURE( paTimedOut ); } } else PA_ENSURE( AlsaStart( stream, 0 ) );end: return result;error: stream->isActive = 0; goto end;}static PaError AlsaStop( PaAlsaStream *stream, int abort ){ PaError result = paNoError; if( abort ) { if( stream->pcm_playback ) ENSURE( snd_pcm_drop( stream->pcm_playback ), paUnanticipatedHostError ); if( stream->pcm_capture && !stream->pcmsSynced ) ENSURE( snd_pcm_drop( stream->pcm_capture ), paUnanticipatedHostError ); PA_DEBUG(( "Dropped frames\n" )); } else { if( stream->pcm_playback ) ENSURE( snd_pcm_drain( stream->pcm_playback ), paUnanticipatedHostError ); if( stream->pcm_capture && !stream->pcmsSynced ) ENSURE( snd_pcm_drain( stream->pcm_capture ), paUnanticipatedHostError ); }end: return result;error: goto end;}/*! \brief Stop or abort stream * * If a stream is in callback mode we will have to inspect wether the background thread has * finished, or we will have to take it out. In either case we join the thread before * returning. In blocking mode, we simply tell ALSA to stop abruptly (abort) or finish * buffers (drain) * * Stream will be considered inactive (!PaAlsaStream::isActive) after a call to this function */static PaError RealStop( PaStream *s, int abort ){ PaError result = paNoError; PaAlsaStream *stream = (PaAlsaStream*)s; /* These two are used to obtain return value when joining thread */ void *pret; int retVal; /* First deal with the callback thread, cancelling and/or joining * it if necessary */ if( stream->callback_mode ) { int res = 0; if( stream->callback_finished ) { res = pthread_join( stream->callback_thread, &pret ); /* Just wait for it to die */ if( pret ) /* Message from dying thread */ { retVal = *(PaError *) pret; free( pret ); PA_ENSURE( retVal ); } } else { /* We are running in callback mode, and the callback thread * is still running. Cancel it and wait for it to be done. */ PA_DEBUG(( "RealStop: Canceling thread\n" )); pthread_cancel( stream->callback_thread ); /* Snuff it! */ res = pthread_join( stream->callback_thread, NULL ); /* XXX: Some way to obtain return value from cancelled thread? */ } stream->callback_finished = 0; if( res != 0 ) /* Pthread call went wrong */ PA_ENSURE( paInternalError ); } else { PA_ENSURE( AlsaStop( stream, abort ) ); } stream->isActive = 0;end: return result;error: goto end;}static PaError StopStream( PaStream *s ){ ((PaAlsaStream *) s)->callbackAbort = 0; /* In case abort has been called earlier */ return RealStop( s, 0);}static PaError AbortStream( PaStream *s ){ ((PaAlsaStream *) s)->callbackAbort = 1; return RealStop( s, 1);}/*! * The stream is considered stopped before StartStream, or AFTER a call to Abort/StopStream (callback * returning !paContinue is not considered) */static PaError IsStreamStopped( PaStream *s ){ PaAlsaStream *stream = (PaAlsaStream *)s; PaError res; /* callback_finished indicates we need to join callback thread (ie. in Abort/StopStream) */ res = !IsStreamActive( s ) && !stream->callback_finished; return res;}static PaError IsStreamActive( PaStream *s ){ PaAlsaStream *stream = (PaAlsaStream*)s; return stream->isActive;}static PaTime GetStreamTime( PaStream *s ){ PaAlsaStream *stream = (PaAlsaStream*)s; snd_timestamp_t timestamp; snd_pcm_status_t *status; snd_pcm_status_alloca( &status ); /* TODO: what if we have both? does it really matter? */ /* TODO: if running in callback mode, this will mean * libasound routines are being called form multiple threads. * need to verify that libasound is thread-safe. */ if( stream->pcm_capture ) { snd_pcm_status( stream->pcm_capture, status ); } else if( stream->pcm_playback ) { snd_pcm_status( stream->pcm_playback, status ); } snd_pcm_status_get_tstamp( status, ×tamp ); PA_DEBUG(( "Time in secs: %d\n", timestamp.tv_sec )); return timestamp.tv_sec + (PaTime) timestamp.tv_usec/1000000;}static double GetStreamCpuLoad( PaStream* s ){ PaAlsaStream *stream = (PaAlsaStream*)s; return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );}/*! * \brief Free resources associated with stream, and eventually stream itself * * Frees allocated memory, and closes opened pcms. */void CleanUpStream( PaAlsaStream *stream ){ assert( stream ); if( stream->pcm_capture ) { snd_pcm_close( stream->pcm_capture ); } if( stream->pcm_playback ) { snd_pcm_close( stream->pcm_playback ); } PaUtil_FreeMemory( stream->pfds ); pthread_mutex_destroy( &stream->stateMtx ); pthread_mutex_destroy( &stream->startMtx ); pthread_cond_destroy( &stream->startCond ); PaUtil_FreeMemory( stream );}int SetApproximateSampleRate( snd_pcm_t *pcm, snd_pcm_hw_params_t *hwParams, double sampleRate ){ unsigned long approx = (unsigned long) sampleRate; int dir = 0; double fraction = sampleRate - approx; assert( pcm && hwParams ); if( fraction > 0.0 ) { if( fraction > 0.5 ) { ++approx; dir = -1; } else dir = 1; } return snd_pcm_hw_params_set_rate( pcm, hwParams, approx, dir );}/* Return exact sample rate in param sampleRate */int GetExactSampleRate( snd_pcm_hw_params_t *hwParams, double *sampleRate ){ unsigned int num, den; int err; assert( hwParams ); err = snd_pcm_hw_params_get_rate_numden( hwParams, &num, &den ); *sampleRate = (double) num / den; return err;}/* Utility functions for blocking/callback interfaces *//* Atomic restart of stream (we don't want the intermediate state visible) */static PaError AlsaRestart( PaAlsaStream *stream ){ PaError result = paNoError; PA_DEBUG(( "Restarting audio\n" )); UNLESS( !pthread_mutex_lock( &stream->stateMtx ), paInternalError ); PA_ENSURE( AlsaStop( stream, 0 ) ); PA_ENSURE( AlsaStart( stream, 0 ) ); PA_DEBUG(( "Restarted audio\n" ));end: if( pthread_mutex_unlock( &stream->stateMtx ) != 0 ) result = paInternalError; return result;error: goto end;}static PaError HandleXrun( PaAlsaStream *stream ){ PaError result = paNoError; snd_pcm_status_t *st; PaTime now = PaUtil_GetTime(); snd_timestamp_t t; snd_pcm_status_alloca( &st ); if( stream->pcm_playback ) { snd_pcm_status( stream->pcm_playback, st ); if( snd_pcm_status_get_state( st ) == SND_PCM_STATE_XRUN ) { snd_pcm_status_get_trigger_tstamp( st, &t ); stream->underrun = now * 1000 - ((PaTime) t.tv_sec * 1000 + (PaTime) t.tv_usec / 1000); } } if( stream->pcm_capture ) { snd_pcm_status( stream->pcm_capture, st ); if( snd_pcm_status_get_state( st ) == SND_PCM_STATE_XRUN ) { snd_pcm_status_get_trigger_tstamp( st, &t ); stream->overrun = now * 1000 - ((PaTime) t.tv_sec * 1000 + (PaTime) t.tv_usec / 1000); } } PA_ENSURE( AlsaRestart( stream ) );end: return result;error: goto end;}/*! \brief Poll on I/O filedescriptors Poll till we've determined there's data for read or write. In the full-duplex case, we don't want to hang around forever waiting for either input or output frames, so whenever we have a timed out filedescriptor we check if we're nearing under/overrun for the other pcm (critical limit set at one buffer). If so, we exit the waiting state, and go on with what we got. */static PaError Wait( PaAlsaStream *stream, snd_pcm_uframes_t *frames ){ PaError result = paNoError; int pollPlayback = 0, pollCapture = 0; snd_pcm_sframes_t captureAvail = INT_MAX, playbackAvail = INT_MAX, commonAvail; int xrun = 0; /* Under/overrun? */ assert( stream && frames && stream->pollTimeout > 0 ); if( stream->pcm_capture ) pollCapture = 1; if( stream->pcm_playback ) poll
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -