pa_linux_alsa.c
来自「mediastreamer2是开源的网络传输媒体流的库」· C语言 代码 · 共 1,651 行 · 第 1/5 页
C
1,651 行
snd_pcm_hw_params_any( pcm, hwParams ); *canMmap = snd_pcm_hw_params_test_access( pcm, hwParams, SND_PCM_ACCESS_MMAP_INTERLEAVED ) >= 0 || snd_pcm_hw_params_test_access( pcm, hwParams, SND_PCM_ACCESS_MMAP_NONINTERLEAVED ) >= 0; if( defaultSr >= 0 ) { /* Could be that the device opened in one mode supports samplerates that the other mode wont have, * so try again .. */ if( SetApproximateSampleRate( pcm, hwParams, defaultSr ) < 0 ) { defaultSr = -1.; PA_DEBUG(( "%s: Original default samplerate failed, trying again ..\n", __FUNCTION__ )); } } if( defaultSr < 0. ) /* Default sample rate not set */ { unsigned int sampleRate = 44100; /* Will contain approximate rate returned by alsa-lib */ if( snd_pcm_hw_params_set_rate_near( pcm, hwParams, &sampleRate, NULL ) < 0) { result = paUnanticipatedHostError; goto error; } ENSURE_( GetExactSampleRate( hwParams, &defaultSr ), paUnanticipatedHostError ); } ENSURE_( snd_pcm_hw_params_get_channels_min( hwParams, &minChans ), paUnanticipatedHostError ); ENSURE_( snd_pcm_hw_params_get_channels_max( hwParams, &maxChans ), paUnanticipatedHostError ); assert( maxChans <= INT_MAX ); assert( maxChans > 0 ); /* Weird linking issue could cause wrong version of ALSA symbols to be called, resulting in zeroed values */ /* XXX: Limit to sensible number (ALSA plugins accept a crazy amount of channels)? */ if( isPlug && maxChans > 128 ) { maxChans = 128; PA_DEBUG(( "%s: Limiting number of plugin channels to %u\n", __FUNCTION__, maxChans )); } /* TWEAKME: * * Giving values for default min and max latency is not * straightforward. Here are our objectives: * * * for low latency, we want to give the lowest value * that will work reliably. This varies based on the * sound card, kernel, CPU, etc. I think it is better * to give sub-optimal latency than to give a number * too low and cause dropouts. My conservative * estimate at this point is to base it on 4096-sample * latency at 44.1 kHz, which gives a latency of 23ms. * * for high latency we want to give a large enough * value that dropouts are basically impossible. This * doesn't really require as much tweaking, since * providing too large a number will just cause us to * select the nearest setting that will work at stream * config time. */ ENSURE_( snd_pcm_hw_params_set_buffer_size_near( pcm, hwParams, &lowLatency ), paUnanticipatedHostError ); /* Have to reset hwParams, to set new buffer size */ ENSURE_( snd_pcm_hw_params_any( pcm, hwParams ), paUnanticipatedHostError ); ENSURE_( snd_pcm_hw_params_set_buffer_size_near( pcm, hwParams, &highLatency ), paUnanticipatedHostError ); *minChannels = (int)minChans; *maxChannels = (int)maxChans; *defaultSampleRate = defaultSr; *defaultLowLatency = (double) lowLatency / *defaultSampleRate; *defaultHighLatency = (double) highLatency / *defaultSampleRate;end: snd_pcm_close( pcm ); return result;error: goto end;}/* Initialize device info with invalid values (maxInputChannels and maxOutputChannels are set to zero since these indicate * wether input/output is available) */static void InitializeDeviceInfo( PaDeviceInfo *deviceInfo ){ deviceInfo->structVersion = -1; deviceInfo->name = NULL; deviceInfo->hostApi = -1; deviceInfo->maxInputChannels = 0; deviceInfo->maxOutputChannels = 0; deviceInfo->defaultLowInputLatency = -1.; deviceInfo->defaultLowOutputLatency = -1.; deviceInfo->defaultHighInputLatency = -1.; deviceInfo->defaultHighOutputLatency = -1.; deviceInfo->defaultSampleRate = -1.;}/* Helper struct */typedef struct{ char *alsaName; char *name; int isPlug; int hasPlayback; int hasCapture;} DeviceNames;static PaError PaAlsa_StrDup( PaAlsaHostApiRepresentation *alsaApi, char **dst, const char *src){ PaError result = paNoError; int len = strlen( src ) + 1; /* PA_DEBUG(("PaStrDup %s %d\n", src, len)); */ PA_UNLESS( *dst = (char *)PaUtil_GroupAllocateMemory( alsaApi->allocations, len ), paInsufficientMemory ); strncpy( *dst, src, len );error: return result;}/* Disregard some standard plugins */static int IgnorePlugin( const char *pluginId ){ /* XXX: dmix and default ignored because after opening and closing, they seem to keep hogging resources. */ static const char *ignoredPlugins[] = {"hw", "plughw", "plug", "dsnoop", "tee", "file", "null", "shm", "cards", "dmix", "default", NULL}; int i = 0; while( ignoredPlugins[i] ) { if( !strcmp( pluginId, ignoredPlugins[i] ) ) { return 1; } ++i; } return 0;}/* Build PaDeviceInfo list, ignore devices for which we cannot determine capabilities (possibly busy, sigh) */static PaError BuildDeviceList( PaAlsaHostApiRepresentation *alsaApi ){ PaUtilHostApiRepresentation *baseApi = &alsaApi->baseHostApiRep; PaAlsaDeviceInfo *deviceInfoArray; int cardIdx = -1, devIdx = 0; snd_ctl_card_info_t *cardInfo; PaError result = paNoError; size_t numDeviceNames = 0, maxDeviceNames = 1, i; DeviceNames *deviceNames = NULL; snd_config_t *topNode = NULL; snd_pcm_info_t *pcmInfo; int res; int blocking = SND_PCM_NONBLOCK; char alsaCardName[50]; if( getenv( "PA_ALSA_INITIALIZE_BLOCK" ) && atoi( getenv( "PA_ALSA_INITIALIZE_BLOCK" ) ) ) blocking = 0; /* These two will be set to the first working input and output device, respectively */ baseApi->info.defaultInputDevice = paNoDevice; baseApi->info.defaultOutputDevice = paNoDevice; /* count the devices by enumerating all the card numbers */ /* snd_card_next() modifies the integer passed to it to be: * the index of the first card if the parameter is -1 * the index of the next card if the parameter is the index of a card * -1 if there are no more cards * * The function itself returns 0 if it succeeded. */ cardIdx = -1; snd_ctl_card_info_alloca( &cardInfo ); snd_pcm_info_alloca( &pcmInfo ); while( snd_card_next( &cardIdx ) == 0 && cardIdx >= 0 ) { char *cardName; int devIdx = -1; snd_ctl_t *ctl; char buf[50]; snprintf( alsaCardName, sizeof (alsaCardName), "hw:%d", cardIdx ); /* Acquire name of card */ if( snd_ctl_open( &ctl, alsaCardName, 0 ) < 0 ) { /* Unable to open card :( */ PA_DEBUG(( "%s: Unable to open device %s\n", __FUNCTION__, alsaCardName )); continue; } snd_ctl_card_info( ctl, cardInfo ); PA_ENSURE( PaAlsa_StrDup( alsaApi, &cardName, snd_ctl_card_info_get_name( cardInfo )) ); while( snd_ctl_pcm_next_device( ctl, &devIdx ) == 0 && devIdx >= 0 ) { char *alsaDeviceName, *deviceName; size_t len; int hasPlayback = 0, hasCapture = 0; snprintf( buf, sizeof (buf), "%s:%d,%d", "hw", cardIdx, devIdx ); /* Obtain info about this particular device */ snd_pcm_info_set_device( pcmInfo, devIdx ); snd_pcm_info_set_subdevice( pcmInfo, 0 ); snd_pcm_info_set_stream( pcmInfo, SND_PCM_STREAM_CAPTURE ); if( snd_ctl_pcm_info( ctl, pcmInfo ) >= 0 ) { hasCapture = 1; } snd_pcm_info_set_stream( pcmInfo, SND_PCM_STREAM_PLAYBACK ); if( snd_ctl_pcm_info( ctl, pcmInfo ) >= 0 ) { hasPlayback = 1; } if( !hasPlayback && !hasCapture ) { continue; /* Error */ } /* The length of the string written by snprintf plus terminating 0 */ len = snprintf( NULL, 0, "%s: %s (%s)", cardName, snd_pcm_info_get_name( pcmInfo ), buf ) + 1; PA_UNLESS( deviceName = (char *)PaUtil_GroupAllocateMemory( alsaApi->allocations, len ), paInsufficientMemory ); snprintf( deviceName, len, "%s: %s (%s)", cardName, snd_pcm_info_get_name( pcmInfo ), buf ); ++numDeviceNames; if( !deviceNames || numDeviceNames > maxDeviceNames ) { maxDeviceNames *= 2; PA_UNLESS( deviceNames = (DeviceNames *) realloc( deviceNames, maxDeviceNames * sizeof (DeviceNames) ), paInsufficientMemory ); } PA_ENSURE( PaAlsa_StrDup( alsaApi, &alsaDeviceName, buf ) ); deviceNames[ numDeviceNames - 1 ].alsaName = alsaDeviceName; deviceNames[ numDeviceNames - 1 ].name = deviceName; deviceNames[ numDeviceNames - 1 ].isPlug = 0; deviceNames[ numDeviceNames - 1 ].hasPlayback = hasPlayback; deviceNames[ numDeviceNames - 1 ].hasCapture = hasCapture; } snd_ctl_close( ctl ); } /* Iterate over plugin devices */ if( NULL == snd_config ) { /* snd_config_update is called implicitly by some functions, if this hasn't happened snd_config will be NULL (bleh) */ ENSURE_( snd_config_update(), paUnanticipatedHostError ); PA_DEBUG(( "Updating snd_config\n" )); } assert( snd_config ); if( (res = snd_config_search( snd_config, "pcm", &topNode )) >= 0 ) { snd_config_iterator_t i, next; snd_config_for_each( i, next, topNode ) { const char *tpStr = "unknown", *idStr = NULL; int err = 0; char *alsaDeviceName, *deviceName; snd_config_t *n = snd_config_iterator_entry( i ), * tp = NULL;; if( (err = snd_config_search( n, "type", &tp )) < 0 ) { if( -ENOENT != err ) { ENSURE_(err, paUnanticipatedHostError); } } else { ENSURE_( snd_config_get_string( tp, &tpStr ), paUnanticipatedHostError ); } ENSURE_( snd_config_get_id( n, &idStr ), paUnanticipatedHostError ); if( IgnorePlugin( idStr ) ) { PA_DEBUG(( "%s: Ignoring ALSA plugin device %s of type %s\n", __FUNCTION__, idStr, tpStr )); continue; } PA_DEBUG(( "%s: Found plugin %s of type %s\n", __FUNCTION__, idStr, tpStr )); PA_UNLESS( alsaDeviceName = (char*)PaUtil_GroupAllocateMemory( alsaApi->allocations, strlen(idStr) + 6 ), paInsufficientMemory ); strcpy( alsaDeviceName, idStr ); PA_UNLESS( deviceName = (char*)PaUtil_GroupAllocateMemory( alsaApi->allocations, strlen(idStr) + 1 ), paInsufficientMemory ); strcpy( deviceName, idStr ); ++numDeviceNames; if( !deviceNames || numDeviceNames > maxDeviceNames ) { maxDeviceNames *= 2; PA_UNLESS( deviceNames = (DeviceNames *) realloc( deviceNames, maxDeviceNames * sizeof (DeviceNames) ), paInsufficientMemory ); } deviceNames[numDeviceNames - 1].alsaName = alsaDeviceName; deviceNames[numDeviceNames - 1].name = deviceName; deviceNames[numDeviceNames - 1].isPlug = 1; deviceNames[numDeviceNames - 1].hasPlayback = 1; deviceNames[numDeviceNames - 1].hasCapture = 1; } } else PA_DEBUG(( "%s: Iterating over ALSA plugins failed: %s\n", __FUNCTION__, snd_strerror( res ) )); /* allocate deviceInfo memory based on the number of devices */ PA_UNLESS( baseApi->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory( alsaApi->allocations, sizeof(PaDeviceInfo*) * (numDeviceNames) ), paInsufficientMemory ); /* allocate all device info structs in a contiguous block */ PA_UNLESS( deviceInfoArray = (PaAlsaDeviceInfo*)PaUtil_GroupAllocateMemory( alsaApi->allocations, sizeof(PaAlsaDeviceInfo) * numDeviceNames ), paInsufficientMemory ); /* Loop over list of cards, filling in info, if a device is deemed unavailable (can't get name), * it's ignored. */ /* while( snd_card_next( &cardIdx ) == 0 && cardIdx >= 0 ) */ for( i = 0, devIdx = 0; i < numDeviceNames; ++i ) { snd_pcm_t *pcm; PaAlsaDeviceInfo *deviceInfo = &deviceInfoArray[devIdx]; PaDeviceInfo *baseDeviceInfo = &deviceInfo->baseDeviceInfo; int canMmap = -1;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?