pa_linux_alsa.c

来自「基于sip协议的网络电话源码」· C语言 代码 · 共 1,692 行 · 第 1/5 页

C
1,692
字号
/* * $Id: pa_linux_alsa.c,v 1.1.2.71 2005/04/15 18:20:18 aknudsen Exp $ * PortAudio Portable Real-Time Audio Library * Latest Version at: http://www.portaudio.com * ALSA implementation by Joshua Haberman and Arve Knudsen * * Copyright (c) 2002 Joshua Haberman <joshua@haberman.com> * Copyright (c) 2005 Arve Knudsen <aknuds-1@broadpark.no> * * Based on the Open Source API proposed by Ross Bencina * Copyright (c) 1999-2002 Ross Bencina, Phil Burk * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files * (the "Software"), to deal in the Software without restriction, * including without limitation the rights to use, copy, modify, merge, * publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * Any person wishing to distribute modifications to the Software is * requested to send the modifications to the original developer so that * they can be incorporated into the canonical version. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */#define ALSA_PCM_NEW_HW_PARAMS_API#define ALSA_PCM_NEW_SW_PARAMS_API#include <alsa/asoundlib.h>#undef ALSA_PCM_NEW_HW_PARAMS_API#undef ALSA_PCM_NEW_SW_PARAMS_API#include <sys/poll.h>#include <string.h> /* strlen() */#include <limits.h>#include <math.h>#include <pthread.h>#include <signal.h>#include <time.h>#include <sys/mman.h>#include <signal.h> /* For sig_atomic_t */#include "portaudio.h"#include "pa_util.h"#include "pa_unix_util.h"#include "pa_allocation.h"#include "pa_hostapi.h"#include "pa_stream.h"#include "pa_cpuload.h"#include "pa_process.h"#include "pa_linux_alsa.h"/* Check return value of ALSA function, and map it to PaError */#define ENSURE_(expr, code) \    do { \        if( UNLIKELY( (aErr_ = (expr)) < 0 ) ) \        { \            /* PaUtil_SetLastHostErrorInfo should only be used in the main thread */ \            if( (code) == paUnanticipatedHostError && pthread_self() != callbackThread_ ) \            { \                PaUtil_SetLastHostErrorInfo( paALSA, aErr_, snd_strerror( aErr_ ) ); \            } \            PaUtil_DebugPrint(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \            result = (code); \            goto error; \        } \    } while( 0 );#define ASSERT_CALL_(expr, success) \    aErr_ = (expr); \    assert( aErr_ == success );static int aErr_;               /* Used with ENSURE_ */static pthread_t callbackThread_;typedef enum{    StreamDirection_In,    StreamDirection_Out} StreamDirection;/* Threading utility struct */typedef struct PaAlsaThreading{    pthread_t watchdogThread;    pthread_t callbackThread;    int watchdogRunning;    int rtSched;    int rtPrio;    int useWatchdog;    unsigned long throttledSleepTime;    volatile PaTime callbackTime;    volatile PaTime callbackCpuTime;    PaUtilCpuLoadMeasurer *cpuLoadMeasurer;} PaAlsaThreading;typedef struct{    PaSampleFormat hostSampleFormat;    unsigned long framesPerBuffer;    int numUserChannels, numHostChannels;    int userInterleaved, hostInterleaved;    snd_pcm_t *pcm;    snd_pcm_uframes_t bufferSize;    snd_pcm_format_t nativeFormat;    unsigned int nfds;    int ready;  /* Marked ready from poll */    void **userBuffers;    snd_pcm_uframes_t offset;    StreamDirection streamDir;    snd_pcm_channel_area_t *channelAreas;  /* Needed for channel adaption */} PaAlsaStreamComponent;/* Implementation specific stream structure */typedef struct PaAlsaStream{    PaUtilStreamRepresentation streamRepresentation;    PaUtilCpuLoadMeasurer cpuLoadMeasurer;    PaUtilBufferProcessor bufferProcessor;    PaAlsaThreading threading;    unsigned long framesPerUserBuffer;    int primeBuffers;    int callbackMode;              /* bool: are we running in callback mode? */    int pcmsSynced;	            /* Have we successfully synced pcms */    /* the callback thread uses these to poll the sound device(s), waiting     * for data to be ready/available */    struct pollfd *pfds;    int pollTimeout;    /* Used in communication between threads */    volatile sig_atomic_t callback_finished; /* bool: are we in the "callback finished" state? */    volatile sig_atomic_t callbackAbort;    /* Drop frames? */    volatile sig_atomic_t callbackStop;     /* Signal a stop */    volatile sig_atomic_t isActive;         /* Is stream in active state? (Between StartStream and StopStream || !paContinue) */    pthread_mutex_t stateMtx;               /* Used to synchronize access to stream state */    pthread_mutex_t startMtx;               /* Used to synchronize stream start in callback mode */    pthread_cond_t startCond;               /* Wait untill audio is started in callback thread */    int neverDropInput;    PaTime underrun;    PaTime overrun;    PaAlsaStreamComponent capture, playback;}PaAlsaStream;/* PaAlsaHostApiRepresentation - host api datastructure specific to this implementation */typedef struct PaAlsaHostApiRepresentation{    PaUtilHostApiRepresentation commonHostApiRep;    PaUtilStreamInterface callbackStreamInterface;    PaUtilStreamInterface blockingStreamInterface;    PaUtilAllocationGroup *allocations;    PaHostApiIndex hostApiIndex;}PaAlsaHostApiRepresentation;typedef struct PaAlsaDeviceInfo{    PaDeviceInfo commonDeviceInfo;    char *alsaName;    int isPlug;    int minInputChannels;    int minOutputChannels;}PaAlsaDeviceInfo;/* Threading utilities */static void InitializeThreading( PaAlsaThreading *th, PaUtilCpuLoadMeasurer *clm ){    th->watchdogRunning = 0;    th->rtSched = 0;    th->callbackTime = 0;    th->callbackCpuTime = 0;    th->useWatchdog = 1;    th->throttledSleepTime = 0;    th->cpuLoadMeasurer = clm;    th->rtPrio = (sched_get_priority_max( SCHED_FIFO ) - sched_get_priority_min( SCHED_FIFO )) / 2            + sched_get_priority_min( SCHED_FIFO );}static PaError KillCallbackThread( PaAlsaThreading *th, int wait, PaError *exitResult, PaError *watchdogExitResult ){    PaError result = paNoError;    void *pret;    if( exitResult )        *exitResult = paNoError;    if( watchdogExitResult )        *watchdogExitResult = paNoError;    if( th->watchdogRunning )    {        pthread_cancel( th->watchdogThread );        ASSERT_CALL_( pthread_join( th->watchdogThread, &pret ), 0 );        if( pret && pret != PTHREAD_CANCELED )        {            if( watchdogExitResult )                *watchdogExitResult = *(PaError *) pret;            free( pret );        }    }    /* Only kill the thread if it isn't in the process of stopping (flushing adaptation buffers) */    /* TODO: Make join time out */    if( !wait )        pthread_cancel( th->callbackThread );   /* XXX: Safe to call this if the thread has exited on its own? */    ASSERT_CALL_( pthread_join( th->callbackThread, &pret ), 0 );    if( pret && pret != PTHREAD_CANCELED )    {        if( exitResult )            *exitResult = *(PaError *) pret;        free( pret );    }    return result;}static void OnWatchdogExit( void *userData ){    PaAlsaThreading *th = (PaAlsaThreading *) userData;    struct sched_param spm = { 0 };    assert( th );    ASSERT_CALL_( pthread_setschedparam( th->callbackThread, SCHED_OTHER, &spm ), 0 );    /* Lower before exiting */    PA_DEBUG(( "Watchdog exiting\n" ));}static PaError BoostPriority( PaAlsaThreading *th ){    PaError result = paNoError;    struct sched_param spm = { 0 };    spm.sched_priority = th->rtPrio;    assert( th );    if( pthread_setschedparam( th->callbackThread, SCHED_FIFO, &spm ) != 0 )    {        PA_UNLESS( errno == EPERM, paInternalError );  /* Lack permission to raise priority */        PA_DEBUG(( "Failed bumping priority\n" ));        result = 0;    }    else        result = 1; /* Success */error:    return result;}static void *WatchdogFunc( void *userData ){    PaError result = paNoError, *pres = NULL;    int err;    PaAlsaThreading *th = (PaAlsaThreading *) userData;    unsigned intervalMsec = 500;    const PaTime maxSeconds = 3.;   /* Max seconds between callbacks */    PaTime timeThen = PaUtil_GetTime(), timeNow, timeElapsed, cpuTimeThen, cpuTimeNow, cpuTimeElapsed;    double cpuLoad, avgCpuLoad = 0.;    int throttled = 0;    assert( th );    pthread_cleanup_push( &OnWatchdogExit, th );	/* Execute OnWatchdogExit when exiting */    /* Boost priority of callback thread */    PA_ENSURE( result = BoostPriority( th ) );    if( !result )    {        pthread_exit( NULL );   /* Boost failed, might as well exit */    }    cpuTimeThen = th->callbackCpuTime;    {        int policy;        struct sched_param spm = { 0 };        pthread_getschedparam( pthread_self(), &policy, &spm );        PA_DEBUG(( "%s: Watchdog priority is %d\n", __FUNCTION__, spm.sched_priority ));    }    while( 1 )    {        double lowpassCoeff = 0.9, lowpassCoeff1 = 0.99999 - lowpassCoeff;                /* Test before and after in case whatever underlying sleep call isn't interrupted by pthread_cancel */        pthread_testcancel();        Pa_Sleep( intervalMsec );        pthread_testcancel();        if( PaUtil_GetTime() - th->callbackTime > maxSeconds )        {            PA_DEBUG(( "Watchdog: Terminating callback thread\n" ));            /* Tell thread to terminate */            err = pthread_kill( th->callbackThread, SIGKILL );            pthread_exit( NULL );        }        PA_DEBUG(( "%s: PortAudio reports CPU load: %g\n", __FUNCTION__, PaUtil_GetCpuLoad( th->cpuLoadMeasurer ) ));        /* Check if we should throttle, or unthrottle :P */        cpuTimeNow = th->callbackCpuTime;        cpuTimeElapsed = cpuTimeNow - cpuTimeThen;        cpuTimeThen = cpuTimeNow;        timeNow = PaUtil_GetTime();        timeElapsed = timeNow - timeThen;        timeThen = timeNow;        cpuLoad = cpuTimeElapsed / timeElapsed;        avgCpuLoad = avgCpuLoad * lowpassCoeff + cpuLoad * lowpassCoeff1;        /*        if( throttled )            PA_DEBUG(( "Watchdog: CPU load: %g, %g\n", avgCpuLoad, cpuTimeElapsed ));            */        if( PaUtil_GetCpuLoad( th->cpuLoadMeasurer ) > .925 )        {            static int policy;            static struct sched_param spm = { 0 };

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?