Ports Portaudio WMME device timeout system to MacOsX CoreAudio in order to

avoid deadlock when a device is unplugged.
Vincent Lucas committed on Jan 10, 2013
2a20693 from https://github.com/jitsi/libsrc/commits/master/portaudio.zip
This commit is contained in:
Ross Bencina 2016-09-03 22:53:30 +10:00
commit 66bda13f84
3 changed files with 69 additions and 4 deletions

View file

@ -2460,15 +2460,32 @@ static PaError StartStream( PaStream *s )
// it's not clear from appl's docs that this really waits // it's not clear from appl's docs that this really waits
// until all data is flushed. // until all data is flushed.
static ComponentResult BlockWhileAudioUnitIsRunning( AudioUnit audioUnit, AudioUnitElement element ) static ComponentResult BlockWhileAudioUnitIsRunning(
AudioUnit audioUnit,
AudioUnitElement element)
{ {
long waitTime = 100;
// If PaUtil_GetRingBufferWriteAvailable starts repetitively and
// consecutively returning that there is no data available, do eventually
// give up in order to allow the caller to handle such cases.
long totalTimeout = 0;
Boolean isRunning = 1; Boolean isRunning = 1;
while( isRunning ) { while( isRunning ) {
UInt32 s = sizeof( isRunning ); UInt32 s = sizeof( isRunning );
ComponentResult err = AudioUnitGetProperty( audioUnit, kAudioOutputUnitProperty_IsRunning, kAudioUnitScope_Global, element, &isRunning, &s ); ComponentResult err = AudioUnitGetProperty( audioUnit, kAudioOutputUnitProperty_IsRunning, kAudioUnitScope_Global, element, &isRunning, &s );
if( err ) if( err )
return err; return err;
Pa_Sleep( 100 ); Pa_Sleep( waitTime );
// If a timeout is encountered, continue. However, testing has
// shown that it is possible to unplug a device and to wait here
// forever. In order to allow the caller to handle such cases of
// repeated timeouts, do eventually given up.
totalTimeout += waitTime;
if( PA_COREAUDIO_MIN_TIMEOUT_MSEC_ <= totalTimeout)
{
return paTimedOut;
}
} }
return noErr; return noErr;
} }
@ -2481,7 +2498,7 @@ static PaError StopStream( PaStream *s )
VVDBUG(("StopStream()\n")); VVDBUG(("StopStream()\n"));
VDBUG( ("Waiting for BLIO.\n") ); VDBUG( ("Waiting for BLIO.\n") );
waitUntilBlioWriteBufferIsFlushed( &stream->blio ); waitUntilBlioWriteBufferIsFlushed( &stream->blio);
VDBUG( ( "Stopping stream.\n" ) ); VDBUG( ( "Stopping stream.\n" ) );
stream->state = STOPPING; stream->state = STOPPING;
@ -2507,7 +2524,7 @@ static PaError StopStream( PaStream *s )
if( stream->outputUnit ) if( stream->outputUnit )
{ {
ERR_WRAP(AudioOutputUnitStop(stream->outputUnit)); ERR_WRAP(AudioOutputUnitStop(stream->outputUnit));
ERR_WRAP( BlockWhileAudioUnitIsRunning(stream->outputUnit,0) ); ERR_WRAP( BlockWhileAudioUnitIsRunning(stream->outputUnit,0));
ERR_WRAP(AudioUnitReset(stream->outputUnit,kAudioUnitScope_Global,0)); ERR_WRAP(AudioUnitReset(stream->outputUnit,kAudioUnitScope_Global,0));
} }
} }

View file

@ -415,6 +415,10 @@ PaError ReadStream( PaStream* stream,
while( frames > 0 ) { while( frames > 0 ) {
long avail; long avail;
long toRead; long toRead;
// If PaUtil_GetRingBufferReadAvailable starts repetitively and
// consecutively returning that there is no data available, do eventually
// give up in order to allow the caller to handle such cases.
long totalTimeout = 0;
do { do {
avail = PaUtil_GetRingBufferReadAvailable( &blio->inputRingBuffer ); avail = PaUtil_GetRingBufferReadAvailable( &blio->inputRingBuffer );
/* /*
@ -438,6 +442,16 @@ PaError ReadStream( PaStream* stream,
return ret; return ret;
#else #else
Pa_Sleep( PA_MAC_BLIO_BUSY_WAIT_SLEEP_INTERVAL ); Pa_Sleep( PA_MAC_BLIO_BUSY_WAIT_SLEEP_INTERVAL );
// If a timeout is encountered, continue. However, testing has
// shown that it is possible to unplug a device and to wait here
// forever. In order to allow the caller to handle such cases of
// repeated timeouts, do eventually given up.
totalTimeout += PA_MAC_BLIO_BUSY_WAIT_SLEEP_INTERVAL;
if( PA_COREAUDIO_MAX_TIMEOUT_MSEC_ <= totalTimeout)
{
return paTimedOut;
}
#endif #endif
} }
} while( avail == 0 ); } while( avail == 0 );
@ -491,6 +505,10 @@ PaError WriteStream( PaStream* stream,
while( frames > 0 ) { while( frames > 0 ) {
long avail = 0; long avail = 0;
long toWrite; long toWrite;
// If PaUtil_GetRingBufferWriteAvailable starts repetitively and
// consecutively returning that there is no data available, do eventually
// give up in order to allow the caller to handle such cases.
long totalTimeout = 0;
do { do {
avail = PaUtil_GetRingBufferWriteAvailable( &blio->outputRingBuffer ); avail = PaUtil_GetRingBufferWriteAvailable( &blio->outputRingBuffer );
@ -515,6 +533,16 @@ PaError WriteStream( PaStream* stream,
return ret; return ret;
#else #else
Pa_Sleep( PA_MAC_BLIO_BUSY_WAIT_SLEEP_INTERVAL ); Pa_Sleep( PA_MAC_BLIO_BUSY_WAIT_SLEEP_INTERVAL );
// If a timeout is encountered, continue. However, testing has
// shown that it is possible to unplug a device and to wait here
// forever. In order to allow the caller to handle such cases of
// repeated timeouts, do eventually given up.
totalTimeout += PA_MAC_BLIO_BUSY_WAIT_SLEEP_INTERVAL;
if( PA_COREAUDIO_MAX_TIMEOUT_MSEC_ <= totalTimeout)
{
return paTimedOut;
}
#endif #endif
} }
} while( avail == 0 ); } while( avail == 0 );
@ -561,13 +589,30 @@ PaError WriteStream( PaStream* stream,
void waitUntilBlioWriteBufferIsFlushed( PaMacBlio *blio ) void waitUntilBlioWriteBufferIsFlushed( PaMacBlio *blio )
{ {
if( blio->outputRingBuffer.buffer ) { if( blio->outputRingBuffer.buffer ) {
// If PaUtil_GetRingBufferWriteAvailable starts repetitively and
// consecutively returning that there is no data available, do eventually
// give up in order to allow the caller to handle such cases.
long totalTimeout = 0;
long avail = PaUtil_GetRingBufferWriteAvailable( &blio->outputRingBuffer ); long avail = PaUtil_GetRingBufferWriteAvailable( &blio->outputRingBuffer );
while( avail != blio->outputRingBuffer.bufferSize ) { while( avail != blio->outputRingBuffer.bufferSize ) {
if( avail == 0 ) if( avail == 0 )
{
Pa_Sleep( PA_MAC_BLIO_BUSY_WAIT_SLEEP_INTERVAL ); Pa_Sleep( PA_MAC_BLIO_BUSY_WAIT_SLEEP_INTERVAL );
// If a timeout is encountered, continue. However, testing has
// shown that it is possible to unplug a device and to wait here
// forever. In order to allow the caller to handle such cases of
// repeated timeouts, do eventually given up.
totalTimeout += PA_MAC_BLIO_BUSY_WAIT_SLEEP_INTERVAL;
if( PA_COREAUDIO_MIN_TIMEOUT_MSEC_ <= totalTimeout)
{
return paTimedOut;
}
}
avail = PaUtil_GetRingBufferWriteAvailable( &blio->outputRingBuffer ); avail = PaUtil_GetRingBufferWriteAvailable( &blio->outputRingBuffer );
} }
} }
return paNoError;
} }

View file

@ -76,6 +76,9 @@
#define PA_MAC_BLIO_MUTEX #define PA_MAC_BLIO_MUTEX
*/ */
#define PA_COREAUDIO_MIN_TIMEOUT_MSEC_ (1000)
#define PA_COREAUDIO_MAX_TIMEOUT_MSEC_ (2000)
typedef struct { typedef struct {
PaUtilRingBuffer inputRingBuffer; PaUtilRingBuffer inputRingBuffer;
PaUtilRingBuffer outputRingBuffer; PaUtilRingBuffer outputRingBuffer;