Fixed dispose call hanging if player or audio thread are doing sync calls to the UI thread. Better rebuffering on 2G

This commit is contained in:
Thong Nguyen 2014-01-26 20:37:06 +00:00
parent 381795fed6
commit 7608a42d29
3 changed files with 231 additions and 136 deletions

View File

@ -52,6 +52,7 @@
UISlider* slider; UISlider* slider;
UISwitch* repeatSwitch; UISwitch* repeatSwitch;
UIButton* playButton; UIButton* playButton;
UIButton* disposeButton;
UIButton* playFromHTTPButton; UIButton* playFromHTTPButton;
UIButton* queueShortFileButton; UIButton* queueShortFileButton;
UIButton* playFromLocalFileButton; UIButton* playFromLocalFileButton;

View File

@ -71,8 +71,13 @@
[queueShortFileButton setTitle:@"Queue short file" forState:UIControlStateNormal]; [queueShortFileButton setTitle:@"Queue short file" forState:UIControlStateNormal];
playButton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; playButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
playButton.frame = CGRectMake((320 - size.width) / 2, 380, size.width, size.height); playButton.frame = CGRectMake(30, 380, size.width, size.height);
[playButton addTarget:self action:@selector(playButtonPressed) forControlEvents:UIControlEventTouchUpInside]; [playButton addTarget:self action:@selector(playButtonPressed) forControlEvents:UIControlEventTouchUpInside];
disposeButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
disposeButton.frame = CGRectMake((320 - size.width) - 30, 380, size.width, size.height);
[disposeButton addTarget:self action:@selector(disposeButtonPressed) forControlEvents:UIControlEventTouchUpInside];
[disposeButton setTitle:@"Dispose" forState:UIControlStateNormal];
slider = [[UISlider alloc] initWithFrame:CGRectMake(20, 320, 280, 20)]; slider = [[UISlider alloc] initWithFrame:CGRectMake(20, 320, 280, 20)];
slider.continuous = YES; slider.continuous = YES;
@ -98,6 +103,7 @@
[self addSubview:repeatSwitch]; [self addSubview:repeatSwitch];
[self addSubview:label]; [self addSubview:label];
[self addSubview:statusLabel]; [self addSubview:statusLabel];
[self addSubview:disposeButton];
[self setupTimer]; [self setupTimer];
[self updateControls]; [self updateControls];
@ -171,12 +177,22 @@
[self.delegate audioPlayerViewQueueShortFileSelected:self]; [self.delegate audioPlayerViewQueueShortFileSelected:self];
} }
-(void) disposeButtonPressed
{
[audioPlayer dispose];
audioPlayer = nil;
}
-(void) playButtonPressed -(void) playButtonPressed
{ {
if (!audioPlayer) if (!audioPlayer)
{ {
return; return;
} }
[audioPlayer dispose];
return;
if (audioPlayer.state == AudioPlayerStatePaused) if (audioPlayer.state == AudioPlayerStatePaused)
{ {

View File

@ -43,7 +43,7 @@
#define STK_BIT_RATE_ESTIMATION_MIN_PACKETS (64) #define STK_BIT_RATE_ESTIMATION_MIN_PACKETS (64)
#define STK_BUFFERS_NEEDED_TO_START (32) #define STK_BUFFERS_NEEDED_TO_START (32)
#define STK_BUFFERS_NEEDED_WHEN_UNDERUNNING (64) #define STK_BUFFERS_NEEDED_WHEN_UNDERUNNING (128)
#define STK_DEFAULT_READ_BUFFER_SIZE (2 * 1024) #define STK_DEFAULT_READ_BUFFER_SIZE (2 * 1024)
#define STK_DEFAULT_PACKET_BUFFER_SIZE (2048) #define STK_DEFAULT_PACKET_BUFFER_SIZE (2048)
#define STK_FRAMES_MISSED_BEFORE_CONSIDERED_UNDERRUN (1024) #define STK_FRAMES_MISSED_BEFORE_CONSIDERED_UNDERRUN (1024)
@ -119,6 +119,7 @@ AudioQueueBufferRefLookupEntry;
UInt64 audioDataOffset; UInt64 audioDataOffset;
UInt64 audioDataByteCount; UInt64 audioDataByteCount;
UInt32 packetBufferSize; UInt32 packetBufferSize;
volatile BOOL cancel;
volatile int processedPacketsCount; volatile int processedPacketsCount;
volatile int processedPacketsSizeTotal; volatile int processedPacketsSizeTotal;
AudioStreamBasicDescription audioStreamBasicDescription; AudioStreamBasicDescription audioStreamBasicDescription;
@ -250,22 +251,24 @@ AudioQueueBufferRefLookupEntry;
} }
} }
-(BOOL) isDefinitelyIncompatible:(AudioStreamBasicDescription*)basicDescription -(BOOL) isKnownToBeIncompatible:(AudioStreamBasicDescription*)basicDescription
{ {
if (self->audioStreamBasicDescription.mSampleRate == 0)
{
return NO;
}
return (memcmp(&(self->audioStreamBasicDescription), basicDescription, sizeof(*basicDescription)) != 0); return (memcmp(&(self->audioStreamBasicDescription), basicDescription, sizeof(*basicDescription)) != 0);
} }
-(BOOL) couldBeIncompatible:(AudioStreamBasicDescription*)basicDescription -(BOOL) couldBeIncompatible:(AudioStreamBasicDescription*)basicDescription
{ {
if (self->audioStreamBasicDescription.mSampleRate != 0) if (self->audioStreamBasicDescription.mSampleRate == 0)
{ {
if (memcmp(&(self->audioStreamBasicDescription), basicDescription, sizeof(*basicDescription)) != 0) return YES;
{
return YES;
}
} }
return NO; return memcmp(&(self->audioStreamBasicDescription), basicDescription, sizeof(*basicDescription)) != 0;
} }
-(NSString*) description -(NSString*) description
@ -327,6 +330,9 @@ AudioQueueBufferRefLookupEntry;
pthread_mutex_t playerMutex; pthread_mutex_t playerMutex;
pthread_mutex_t queueBuffersMutex; pthread_mutex_t queueBuffersMutex;
pthread_cond_t queueBufferReadyCondition; pthread_cond_t queueBufferReadyCondition;
pthread_mutex_t mainThreadSyncCallMutex;
pthread_cond_t mainThreadSyncCallReadyCondition;
volatile BOOL waiting; volatile BOOL waiting;
volatile BOOL disposeWasRequested; volatile BOOL disposeWasRequested;
@ -400,21 +406,6 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
return internalState; return internalState;
} }
-(void) syncInvokeOnMainThreadWithPlayerLock:(void(^)())block
{
block = [block copy];
[self->asyncApiRequestQueue addOperationWithBlock:^
{
pthread_mutex_lock(&playerMutex);
dispatch_sync(dispatch_get_main_queue(), ^
{
block();
});
pthread_mutex_unlock(&playerMutex);
}];
}
-(void) setInternalState:(AudioPlayerInternalState)value -(void) setInternalState:(AudioPlayerInternalState)value
{ {
if (value == internalState) if (value == internalState)
@ -426,10 +417,10 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
if ([self.delegate respondsToSelector:@selector(audioPlayer:internalStateChanged:)]) if ([self.delegate respondsToSelector:@selector(audioPlayer:internalStateChanged:)])
{ {
[self syncInvokeOnMainThreadWithPlayerLock: ^ dispatch_async(dispatch_get_main_queue(), ^
{ {
[self.delegate audioPlayer:self internalStateChanged:internalState]; [self.delegate audioPlayer:self internalStateChanged:internalState];
}]; });
} }
AudioPlayerState newState; AudioPlayerState newState;
@ -468,10 +459,10 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
{ {
self.state = newState; self.state = newState;
[self syncInvokeOnMainThreadWithPlayerLock: ^ dispatch_async(dispatch_get_main_queue(), ^
{ {
[self.delegate audioPlayer:self stateChanged:self.state]; [self.delegate audioPlayer:self stateChanged:self.state];
}]; });
} }
} }
@ -540,6 +531,9 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
pthread_mutex_init(&playerMutex, &attr); pthread_mutex_init(&playerMutex, &attr);
pthread_mutex_init(&queueBuffersMutex, NULL); pthread_mutex_init(&queueBuffersMutex, NULL);
pthread_cond_init(&queueBufferReadyCondition, NULL); pthread_cond_init(&queueBufferReadyCondition, NULL);
pthread_mutex_init(&mainThreadSyncCallMutex, NULL);
pthread_cond_init(&mainThreadSyncCallReadyCondition, NULL);
threadFinishedCondLock = [[NSConditionLock alloc] initWithCondition:0]; threadFinishedCondLock = [[NSConditionLock alloc] initWithCondition:0];
@ -567,6 +561,10 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
pthread_mutex_destroy(&playerMutex); pthread_mutex_destroy(&playerMutex);
pthread_mutex_destroy(&queueBuffersMutex); pthread_mutex_destroy(&queueBuffersMutex);
pthread_cond_destroy(&queueBufferReadyCondition); pthread_cond_destroy(&queueBufferReadyCondition);
pthread_mutex_destroy(&mainThreadSyncCallMutex);
pthread_cond_destroy(&mainThreadSyncCallReadyCondition);
if (audioFileStream) if (audioFileStream)
{ {
@ -802,62 +800,69 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
AudioQueueSetParameter(audioQueue, kAudioQueueParam_Volume, 1); AudioQueueSetParameter(audioQueue, kAudioQueueParam_Volume, 1);
if (currentlyReadingEntry != currentlyPlayingEntry && audioQueue && currentAudioStreamBasicDescription.mSampleRate != 0) if (currentlyReadingEntry->cancel)
{ {
if (memcmp(&currentAudioStreamBasicDescription, &newBasicDescription, sizeof(currentAudioStreamBasicDescription)) != 0) cancel = YES;
}
else
{
if (currentlyReadingEntry != currentlyPlayingEntry && audioQueue && currentAudioStreamBasicDescription.mSampleRate != 0)
{ {
[currentlyReadingEntry.dataSource unregisterForEvents]; if (memcmp(&currentAudioStreamBasicDescription, &newBasicDescription, sizeof(currentAudioStreamBasicDescription)) != 0)
if ([bufferingQueue objectAtIndex:0] == currentlyReadingEntry)
{ {
[bufferingQueue removeObjectAtIndex:0]; cancel = YES;
} }
STKQueueEntry* newEntry = [[STKQueueEntry alloc] initWithDataSource:currentlyReadingEntry.dataSource andQueueItemId:currentlyReadingEntry.queueItemId];
entryToUpdate = newEntry;
[upcomingQueue skipQueue:newEntry];
OSSpinLockLock(&currentEntryReferencesLock);
currentlyReadingEntry = nil;
OSSpinLockUnlock(&currentEntryReferencesLock);
cancel = YES;
} }
} }
if (!cancel) if (cancel)
{ {
entryToUpdate->audioStreamBasicDescription = newBasicDescription; [currentlyReadingEntry.dataSource unregisterForEvents];
entryToUpdate->sampleRate = entryToUpdate->audioStreamBasicDescription.mSampleRate;
entryToUpdate->packetDuration = entryToUpdate->audioStreamBasicDescription.mFramesPerPacket / entryToUpdate->sampleRate;
UInt32 packetBufferSize = 0;
UInt32 sizeOfPacketBufferSize = sizeof(packetBufferSize);
error = AudioFileStreamGetProperty(audioFileStream, kAudioFileStreamProperty_PacketSizeUpperBound, &sizeOfPacketBufferSize, &packetBufferSize); if ([bufferingQueue objectAtIndex:0] == currentlyReadingEntry)
{
[bufferingQueue removeObjectAtIndex:0];
}
STKQueueEntry* newEntry = [[STKQueueEntry alloc] initWithDataSource:currentlyReadingEntry.dataSource andQueueItemId:currentlyReadingEntry.queueItemId];
entryToUpdate = newEntry;
[upcomingQueue skipQueue:newEntry];
OSSpinLockLock(&currentEntryReferencesLock);
currentlyReadingEntry = nil;
OSSpinLockUnlock(&currentEntryReferencesLock);
}
entryToUpdate->audioStreamBasicDescription = newBasicDescription;
entryToUpdate->sampleRate = entryToUpdate->audioStreamBasicDescription.mSampleRate;
entryToUpdate->packetDuration = entryToUpdate->audioStreamBasicDescription.mFramesPerPacket / entryToUpdate->sampleRate;
UInt32 packetBufferSize = 0;
UInt32 sizeOfPacketBufferSize = sizeof(packetBufferSize);
error = AudioFileStreamGetProperty(audioFileStream, kAudioFileStreamProperty_PacketSizeUpperBound, &sizeOfPacketBufferSize, &packetBufferSize);
if (error || packetBufferSize == 0)
{
error = AudioFileStreamGetProperty(audioFileStream, kAudioFileStreamProperty_MaximumPacketSize, &sizeOfPacketBufferSize, &packetBufferSize);
if (error || packetBufferSize == 0) if (error || packetBufferSize == 0)
{ {
error = AudioFileStreamGetProperty(audioFileStream, kAudioFileStreamProperty_MaximumPacketSize, &sizeOfPacketBufferSize, &packetBufferSize); entryToUpdate->packetBufferSize = STK_DEFAULT_PACKET_BUFFER_SIZE;
if (error || packetBufferSize == 0)
{
entryToUpdate->packetBufferSize = STK_DEFAULT_PACKET_BUFFER_SIZE;
}
else
{
entryToUpdate->packetBufferSize = packetBufferSize;
}
} }
else else
{ {
entryToUpdate->packetBufferSize = packetBufferSize; entryToUpdate->packetBufferSize = packetBufferSize;
} }
[entryToUpdate updateAudioDataSource];
} }
else
{
entryToUpdate->packetBufferSize = packetBufferSize;
}
[entryToUpdate updateAudioDataSource];
pthread_mutex_unlock(&playerMutex); pthread_mutex_unlock(&playerMutex);
} }
@ -936,7 +941,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
return; return;
} }
if (seekToTimeWasRequested) if (seekToTimeWasRequested || disposeWasRequested)
{ {
return; return;
} }
@ -1015,7 +1020,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
{ {
[self enqueueBuffer]; [self enqueueBuffer];
if (seekToTimeWasRequested || self.internalState == AudioPlayerInternalStateStopped || self.internalState == AudioPlayerInternalStateStopping || self.internalState == AudioPlayerInternalStateDisposed) if (disposeWasRequested || seekToTimeWasRequested || self.internalState == AudioPlayerInternalStateStopped || self.internalState == AudioPlayerInternalStateStopping || self.internalState == AudioPlayerInternalStateDisposed)
{ {
return; return;
} }
@ -1050,7 +1055,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
{ {
[self enqueueBuffer]; [self enqueueBuffer];
if (seekToTimeWasRequested || self.internalState == AudioPlayerInternalStateStopped || self.internalState == AudioPlayerInternalStateStopping || self.internalState == AudioPlayerInternalStateDisposed) if (disposeWasRequested || seekToTimeWasRequested || self.internalState == AudioPlayerInternalStateStopped || self.internalState == AudioPlayerInternalStateStopping || self.internalState == AudioPlayerInternalStateDisposed)
{ {
return; return;
} }
@ -1071,7 +1076,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
{ {
[self enqueueBuffer]; [self enqueueBuffer];
if (seekToTimeWasRequested || self.internalState == AudioPlayerInternalStateStopped || self.internalState == AudioPlayerInternalStateStopping || self.internalState == AudioPlayerInternalStateDisposed) if (disposeWasRequested || seekToTimeWasRequested || self.internalState == AudioPlayerInternalStateStopped || self.internalState == AudioPlayerInternalStateStopping || self.internalState == AudioPlayerInternalStateDisposed)
{ {
return; return;
} }
@ -1225,11 +1230,21 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
upcomingExistsAndIsIncompatible = [upcoming couldBeIncompatible:&currentAudioStreamBasicDescription]; upcomingExistsAndIsIncompatible = [upcoming couldBeIncompatible:&currentAudioStreamBasicDescription];
} }
BOOL lastInBufferingQueue; BOOL nextBufferingExistsAndIsIncompatible = NO;
STKQueueEntry* nextBuffering = [bufferingQueue peek];
lastInBufferingQueue = [bufferingQueue peek] == nil; if (nextBuffering != nil)
{
if ([nextBuffering couldBeIncompatible:&currentAudioStreamBasicDescription])
{
nextBufferingExistsAndIsIncompatible = YES;
nextBuffering->cancel = YES;
NSAssert(bufferingQueue.count == 1, @"bufferingQueue.count == 1");
}
}
if ((upcoming == nil || upcomingExistsAndIsIncompatible) && lastInBufferingQueue) if ((upcoming == nil || upcomingExistsAndIsIncompatible) && (nextBuffering == nil || nextBufferingExistsAndIsIncompatible))
{ {
if (self.internalState != AudioPlayerInternalStateFlushingAndStoppingButStillPlaying) if (self.internalState != AudioPlayerInternalStateFlushingAndStoppingButStillPlaying)
{ {
@ -1253,10 +1268,9 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
{ {
if (numberOfBuffersUsed == 0 if (numberOfBuffersUsed == 0
&& !seekToTimeWasRequested && !seekToTimeWasRequested
&& !disposeWasRequested
&& self.internalState != AudioPlayerInternalStateWaitingForData) && self.internalState != AudioPlayerInternalStateWaitingForData)
{ {
LOGINFO(([NSString stringWithFormat:@"UNDERRUN WITH TIME: %f", [self currentTimeInFrames]]));
if (self->rebufferingStartFrames == 0) if (self->rebufferingStartFrames == 0)
{ {
[self invokeOnPlaybackThread:^ [self invokeOnPlaybackThread:^
@ -1264,11 +1278,16 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
pthread_mutex_lock(&playerMutex); pthread_mutex_lock(&playerMutex);
if (self->numberOfBuffersUsed == 0 if (self->numberOfBuffersUsed == 0
&& !disposeWasRequested
&& !seekToTimeWasRequested && !seekToTimeWasRequested
&& self->rebufferingStartFrames == 0 && self->rebufferingStartFrames == 0
&& self.internalState != AudioPlayerInternalStateWaitingForData && self.internalState != AudioPlayerInternalStateWaitingForData
&& [self moreFramesAreDefinitelyAvailableToPlay]) && [self moreFramesAreDefinitelyAvailableToPlay])
{ {
if ([self moreFramesAreDefinitelyAvailableToPlay])
{
}
AudioQueuePause(audioQueue); AudioQueuePause(audioQueue);
self->rebufferingStartFrames = [self currentTimeInFrames]; self->rebufferingStartFrames = [self currentTimeInFrames];
@ -1280,7 +1299,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
}]; }];
} }
if (self.internalState != AudioPlayerStateBuffering && self.internalState != AudioPlayerInternalStatePaused) if (self.internalState != AudioPlayerInternalStateRebuffering && self.internalState != AudioPlayerInternalStatePaused)
{ {
Float64 interval = STK_FRAMES_MISSED_BEFORE_CONSIDERED_UNDERRUN / currentAudioStreamBasicDescription.mSampleRate; Float64 interval = STK_FRAMES_MISSED_BEFORE_CONSIDERED_UNDERRUN / currentAudioStreamBasicDescription.mSampleRate;
@ -1294,6 +1313,8 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
} }
} }
signal = signal || disposeWasRequested;
if (self.internalState == AudioPlayerInternalStateStopped if (self.internalState == AudioPlayerInternalStateStopped
|| self.internalState == AudioPlayerInternalStateStopping || self.internalState == AudioPlayerInternalStateStopping
|| self.internalState == AudioPlayerInternalStateDisposed || self.internalState == AudioPlayerInternalStateDisposed
@ -1368,11 +1389,6 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
return NO; return NO;
} }
if (upcomingQueue.count > 0 && [(STKQueueEntry*)[upcomingQueue peek] isDefinitelyIncompatible:&currentAudioStreamBasicDescription])
{
return YES;
}
return NO; return NO;
} }
@ -1573,7 +1589,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
waiting = YES; waiting = YES;
while (bufferUsed[fillBufferIndex] && !(seekToTimeWasRequested || self.internalState == AudioPlayerInternalStateStopped || self.internalState == AudioPlayerInternalStateStopping || self.internalState == AudioPlayerInternalStateDisposed)) while (bufferUsed[fillBufferIndex] && !(disposeWasRequested || seekToTimeWasRequested || self.internalState == AudioPlayerInternalStateStopped || self.internalState == AudioPlayerInternalStateStopping || self.internalState == AudioPlayerInternalStateDisposed))
{ {
if (numberOfBuffersUsed == 0) if (numberOfBuffersUsed == 0)
{ {
@ -1616,12 +1632,15 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
{ {
LOGINFO(@"AudioQueueStop/1"); LOGINFO(@"AudioQueueStop/1");
pthread_mutex_unlock(&queueBuffersMutex);
AudioQueueStop(audioQueue, YES); AudioQueueStop(audioQueue, YES);
AudioQueueDispose(audioQueue, YES); AudioQueueDispose(audioQueue, YES);
memset(&currentAudioStreamBasicDescription, 0, sizeof(currentAudioStreamBasicDescription)); memset(&currentAudioStreamBasicDescription, 0, sizeof(currentAudioStreamBasicDescription));
audioQueue = nil; audioQueue = nil;
pthread_mutex_lock(&queueBuffersMutex);
} }
if (currentlyPlayingEntry == nil) if (currentlyPlayingEntry == nil)
@ -2068,19 +2087,56 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
} }
} }
-(void) dispatchSyncOnMainThread:(void(^)())block
{
__block BOOL finished = NO;
if (disposeWasRequested)
{
return;
}
dispatch_async(dispatch_get_main_queue(), ^
{
block();
pthread_mutex_lock(&mainThreadSyncCallMutex);
finished = YES;
pthread_cond_signal(&mainThreadSyncCallReadyCondition);
pthread_mutex_unlock(&mainThreadSyncCallMutex);
});
while (true)
{
if (disposeWasRequested)
{
break;
}
if (finished)
{
break;
}
pthread_mutex_lock(&mainThreadSyncCallMutex);
pthread_cond_wait(&mainThreadSyncCallReadyCondition, &mainThreadSyncCallMutex);
pthread_mutex_unlock(&mainThreadSyncCallMutex);
}
}
-(void) playbackThreadQueueMainThreadSyncBlock:(void(^)())block -(void) playbackThreadQueueMainThreadSyncBlock:(void(^)())block
{ {
if ([NSThread currentThread] == playbackThread) block = [block copy];
[self invokeOnPlaybackThread:^
{ {
dispatch_sync(dispatch_get_main_queue(), block); if (disposeWasRequested)
}
else
{
[self invokeOnPlaybackThread:^
{ {
dispatch_sync(dispatch_get_main_queue(), block); return;
}]; }
}
[self dispatchSyncOnMainThread:block];
}];
} }
-(BOOL) processRunloop -(BOOL) processRunloop
@ -2175,7 +2231,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
next = [upcomingQueue peek]; next = [upcomingQueue peek];
if ([next couldBeIncompatible:&currentAudioStreamBasicDescription] && currentlyPlayingEntry != nil) if ([next isKnownToBeIncompatible:&currentAudioStreamBasicDescription] && currentlyPlayingEntry != nil)
{ {
goto endOfIfElse; goto endOfIfElse;
} }
@ -2618,58 +2674,68 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
NSObject* queueItemId = currentlyReadingEntry.queueItemId; NSObject* queueItemId = currentlyReadingEntry.queueItemId;
dispatch_sync(dispatch_get_main_queue(), ^ if (disposeWasRequested)
{
return;
}
[self dispatchSyncOnMainThread:^
{ {
[self.delegate audioPlayer:self didFinishBufferingSourceWithQueueItemId:queueItemId]; [self.delegate audioPlayer:self didFinishBufferingSourceWithQueueItemId:queueItemId];
}); }];
pthread_mutex_lock(&playerMutex); if (disposeWasRequested)
{ {
if (audioQueue) return;
}
pthread_mutex_lock(&playerMutex);
if (audioQueue)
{
currentlyReadingEntry.lastFrameIndex = self->framesQueued;
currentlyReadingEntry.lastByteIndex = audioPacketsReadCount;
if (audioPacketsReadCount == 0 && audioPacketsPlayedCount == audioPacketsReadCount && currentlyReadingEntry == currentlyPlayingEntry)
{ {
currentlyReadingEntry.lastFrameIndex = self->framesQueued; LOGINFO(@"Seeked to end");
currentlyReadingEntry.lastByteIndex = audioPacketsReadCount;
if (audioPacketsReadCount == 0 && audioPacketsPlayedCount == audioPacketsReadCount && currentlyReadingEntry == currentlyPlayingEntry) seekToTimeWasRequested = NO;
if (audioQueue)
{ {
LOGINFO(@"Seeked to end"); if ([self audioQueueIsRunning])
seekToTimeWasRequested = NO;
if (audioQueue)
{ {
if ([self audioQueueIsRunning]) self.internalState = AudioPlayerInternalStateFlushingAndStoppingButStillPlaying;
{
self.internalState = AudioPlayerInternalStateFlushingAndStoppingButStillPlaying;
LOGINFO(@"Stopping AudioQueue asynchronously"); LOGINFO(@"Stopping AudioQueue asynchronously");
if (AudioQueueStop(audioQueue, NO) != 0) if (AudioQueueStop(audioQueue, NO) != 0)
{
LOGINFO(@"Stopping AudioQueue asynchronously failed");
[self processFinishedPlayingViaAudioQueueStop];
}
}
else
{ {
LOGINFO(@"AudioQueue already stopped"); LOGINFO(@"Stopping AudioQueue asynchronously failed");
[self processFinishedPlayingViaAudioQueueStop]; [self processFinishedPlayingViaAudioQueueStop];
} }
} }
else
{
LOGINFO(@"AudioQueue already stopped");
[self processFinishedPlayingViaAudioQueueStop];
}
} }
}
OSSpinLockLock(&currentEntryReferencesLock); OSSpinLockLock(&currentEntryReferencesLock);
currentlyReadingEntry = nil; currentlyReadingEntry = nil;
OSSpinLockUnlock(&currentEntryReferencesLock); OSSpinLockUnlock(&currentEntryReferencesLock);
}
else
{
stopReason = AudioPlayerStopReasonEof;
self.internalState = AudioPlayerInternalStateStopped;
}
} }
else
{
stopReason = AudioPlayerStopReasonEof;
self.internalState = AudioPlayerInternalStateStopped;
}
pthread_mutex_unlock(&playerMutex); pthread_mutex_unlock(&playerMutex);
} }
@ -2786,18 +2852,30 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
{ {
BOOL wait = NO; BOOL wait = NO;
pthread_mutex_lock(&playerMutex); NSRunLoop* runLoop = playbackThreadRunLoop;
if (runLoop != nil)
{ {
disposeWasRequested = YES; wait = YES;
if (playbackThread && playbackThreadRunLoop) [self invokeOnPlaybackThread:^
{ {
wait = YES; pthread_mutex_lock(&playerMutex);
disposeWasRequested = YES;
CFRunLoopStop([playbackThreadRunLoop getCFRunLoop]); pthread_mutex_unlock(&queueBuffersMutex);
} }];
pthread_mutex_lock(&queueBuffersMutex);
pthread_cond_signal(&queueBufferReadyCondition);
pthread_mutex_unlock(&queueBuffersMutex);
pthread_mutex_lock(&mainThreadSyncCallMutex);
pthread_cond_signal(&mainThreadSyncCallReadyCondition);
pthread_mutex_unlock(&mainThreadSyncCallMutex);
CFRunLoopStop([runLoop getCFRunLoop]);
} }
pthread_mutex_unlock(&playerMutex);
if (wait) if (wait)
{ {