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:
parent
381795fed6
commit
7608a42d29
|
|
@ -52,6 +52,7 @@
|
|||
UISlider* slider;
|
||||
UISwitch* repeatSwitch;
|
||||
UIButton* playButton;
|
||||
UIButton* disposeButton;
|
||||
UIButton* playFromHTTPButton;
|
||||
UIButton* queueShortFileButton;
|
||||
UIButton* playFromLocalFileButton;
|
||||
|
|
|
|||
|
|
@ -71,8 +71,13 @@
|
|||
[queueShortFileButton setTitle:@"Queue short file" forState:UIControlStateNormal];
|
||||
|
||||
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];
|
||||
|
||||
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.continuous = YES;
|
||||
|
|
@ -98,6 +103,7 @@
|
|||
[self addSubview:repeatSwitch];
|
||||
[self addSubview:label];
|
||||
[self addSubview:statusLabel];
|
||||
[self addSubview:disposeButton];
|
||||
|
||||
[self setupTimer];
|
||||
[self updateControls];
|
||||
|
|
@ -171,12 +177,22 @@
|
|||
[self.delegate audioPlayerViewQueueShortFileSelected:self];
|
||||
}
|
||||
|
||||
-(void) disposeButtonPressed
|
||||
{
|
||||
[audioPlayer dispose];
|
||||
audioPlayer = nil;
|
||||
}
|
||||
|
||||
-(void) playButtonPressed
|
||||
{
|
||||
if (!audioPlayer)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
[audioPlayer dispose];
|
||||
|
||||
return;
|
||||
|
||||
if (audioPlayer.state == AudioPlayerStatePaused)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@
|
|||
|
||||
#define STK_BIT_RATE_ESTIMATION_MIN_PACKETS (64)
|
||||
#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_PACKET_BUFFER_SIZE (2048)
|
||||
#define STK_FRAMES_MISSED_BEFORE_CONSIDERED_UNDERRUN (1024)
|
||||
|
|
@ -119,6 +119,7 @@ AudioQueueBufferRefLookupEntry;
|
|||
UInt64 audioDataOffset;
|
||||
UInt64 audioDataByteCount;
|
||||
UInt32 packetBufferSize;
|
||||
volatile BOOL cancel;
|
||||
volatile int processedPacketsCount;
|
||||
volatile int processedPacketsSizeTotal;
|
||||
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);
|
||||
}
|
||||
|
||||
-(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
|
||||
|
|
@ -327,6 +330,9 @@ AudioQueueBufferRefLookupEntry;
|
|||
pthread_mutex_t playerMutex;
|
||||
pthread_mutex_t queueBuffersMutex;
|
||||
pthread_cond_t queueBufferReadyCondition;
|
||||
|
||||
pthread_mutex_t mainThreadSyncCallMutex;
|
||||
pthread_cond_t mainThreadSyncCallReadyCondition;
|
||||
|
||||
volatile BOOL waiting;
|
||||
volatile BOOL disposeWasRequested;
|
||||
|
|
@ -400,21 +406,6 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
|
|||
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
|
||||
{
|
||||
if (value == internalState)
|
||||
|
|
@ -426,10 +417,10 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
|
|||
|
||||
if ([self.delegate respondsToSelector:@selector(audioPlayer:internalStateChanged:)])
|
||||
{
|
||||
[self syncInvokeOnMainThreadWithPlayerLock: ^
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
[self.delegate audioPlayer:self internalStateChanged:internalState];
|
||||
}];
|
||||
});
|
||||
}
|
||||
|
||||
AudioPlayerState newState;
|
||||
|
|
@ -468,10 +459,10 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
|
|||
{
|
||||
self.state = newState;
|
||||
|
||||
[self syncInvokeOnMainThreadWithPlayerLock: ^
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
[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(&queueBuffersMutex, NULL);
|
||||
pthread_cond_init(&queueBufferReadyCondition, NULL);
|
||||
|
||||
pthread_mutex_init(&mainThreadSyncCallMutex, NULL);
|
||||
pthread_cond_init(&mainThreadSyncCallReadyCondition, NULL);
|
||||
|
||||
threadFinishedCondLock = [[NSConditionLock alloc] initWithCondition:0];
|
||||
|
||||
|
|
@ -567,6 +561,10 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
|
|||
pthread_mutex_destroy(&playerMutex);
|
||||
pthread_mutex_destroy(&queueBuffersMutex);
|
||||
pthread_cond_destroy(&queueBufferReadyCondition);
|
||||
|
||||
pthread_mutex_destroy(&mainThreadSyncCallMutex);
|
||||
pthread_cond_destroy(&mainThreadSyncCallReadyCondition);
|
||||
|
||||
|
||||
if (audioFileStream)
|
||||
{
|
||||
|
|
@ -802,62 +800,69 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
|
|||
|
||||
AudioQueueSetParameter(audioQueue, kAudioQueueParam_Volume, 1);
|
||||
|
||||
if (currentlyReadingEntry != currentlyPlayingEntry && audioQueue && currentAudioStreamBasicDescription.mSampleRate != 0)
|
||||
if (currentlyReadingEntry->cancel)
|
||||
{
|
||||
if (memcmp(¤tAudioStreamBasicDescription, &newBasicDescription, sizeof(currentAudioStreamBasicDescription)) != 0)
|
||||
cancel = YES;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (currentlyReadingEntry != currentlyPlayingEntry && audioQueue && currentAudioStreamBasicDescription.mSampleRate != 0)
|
||||
{
|
||||
[currentlyReadingEntry.dataSource unregisterForEvents];
|
||||
|
||||
if ([bufferingQueue objectAtIndex:0] == currentlyReadingEntry)
|
||||
if (memcmp(¤tAudioStreamBasicDescription, &newBasicDescription, sizeof(currentAudioStreamBasicDescription)) != 0)
|
||||
{
|
||||
[bufferingQueue removeObjectAtIndex:0];
|
||||
cancel = YES;
|
||||
}
|
||||
|
||||
STKQueueEntry* newEntry = [[STKQueueEntry alloc] initWithDataSource:currentlyReadingEntry.dataSource andQueueItemId:currentlyReadingEntry.queueItemId];
|
||||
|
||||
entryToUpdate = newEntry;
|
||||
|
||||
[upcomingQueue skipQueue:newEntry];
|
||||
|
||||
OSSpinLockLock(¤tEntryReferencesLock);
|
||||
currentlyReadingEntry = nil;
|
||||
OSSpinLockUnlock(¤tEntryReferencesLock);
|
||||
|
||||
cancel = YES;
|
||||
}
|
||||
}
|
||||
|
||||
if (!cancel)
|
||||
if (cancel)
|
||||
{
|
||||
entryToUpdate->audioStreamBasicDescription = newBasicDescription;
|
||||
entryToUpdate->sampleRate = entryToUpdate->audioStreamBasicDescription.mSampleRate;
|
||||
entryToUpdate->packetDuration = entryToUpdate->audioStreamBasicDescription.mFramesPerPacket / entryToUpdate->sampleRate;
|
||||
|
||||
UInt32 packetBufferSize = 0;
|
||||
UInt32 sizeOfPacketBufferSize = sizeof(packetBufferSize);
|
||||
[currentlyReadingEntry.dataSource unregisterForEvents];
|
||||
|
||||
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(¤tEntryReferencesLock);
|
||||
currentlyReadingEntry = nil;
|
||||
OSSpinLockUnlock(¤tEntryReferencesLock);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
error = AudioFileStreamGetProperty(audioFileStream, kAudioFileStreamProperty_MaximumPacketSize, &sizeOfPacketBufferSize, &packetBufferSize);
|
||||
|
||||
if (error || packetBufferSize == 0)
|
||||
{
|
||||
entryToUpdate->packetBufferSize = STK_DEFAULT_PACKET_BUFFER_SIZE;
|
||||
}
|
||||
else
|
||||
{
|
||||
entryToUpdate->packetBufferSize = packetBufferSize;
|
||||
}
|
||||
entryToUpdate->packetBufferSize = STK_DEFAULT_PACKET_BUFFER_SIZE;
|
||||
}
|
||||
else
|
||||
{
|
||||
entryToUpdate->packetBufferSize = packetBufferSize;
|
||||
}
|
||||
|
||||
[entryToUpdate updateAudioDataSource];
|
||||
}
|
||||
else
|
||||
{
|
||||
entryToUpdate->packetBufferSize = packetBufferSize;
|
||||
}
|
||||
|
||||
[entryToUpdate updateAudioDataSource];
|
||||
|
||||
pthread_mutex_unlock(&playerMutex);
|
||||
}
|
||||
|
|
@ -936,7 +941,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
|
|||
return;
|
||||
}
|
||||
|
||||
if (seekToTimeWasRequested)
|
||||
if (seekToTimeWasRequested || disposeWasRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
@ -1015,7 +1020,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
|
|||
{
|
||||
[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;
|
||||
}
|
||||
|
|
@ -1050,7 +1055,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
|
|||
{
|
||||
[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;
|
||||
}
|
||||
|
|
@ -1071,7 +1076,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
|
|||
{
|
||||
[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;
|
||||
}
|
||||
|
|
@ -1225,11 +1230,21 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
|
|||
upcomingExistsAndIsIncompatible = [upcoming couldBeIncompatible:¤tAudioStreamBasicDescription];
|
||||
}
|
||||
|
||||
BOOL lastInBufferingQueue;
|
||||
BOOL nextBufferingExistsAndIsIncompatible = NO;
|
||||
STKQueueEntry* nextBuffering = [bufferingQueue peek];
|
||||
|
||||
lastInBufferingQueue = [bufferingQueue peek] == nil;
|
||||
if (nextBuffering != nil)
|
||||
{
|
||||
if ([nextBuffering couldBeIncompatible:¤tAudioStreamBasicDescription])
|
||||
{
|
||||
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)
|
||||
{
|
||||
|
|
@ -1253,10 +1268,9 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
|
|||
{
|
||||
if (numberOfBuffersUsed == 0
|
||||
&& !seekToTimeWasRequested
|
||||
&& !disposeWasRequested
|
||||
&& self.internalState != AudioPlayerInternalStateWaitingForData)
|
||||
{
|
||||
LOGINFO(([NSString stringWithFormat:@"UNDERRUN WITH TIME: %f", [self currentTimeInFrames]]));
|
||||
|
||||
if (self->rebufferingStartFrames == 0)
|
||||
{
|
||||
[self invokeOnPlaybackThread:^
|
||||
|
|
@ -1264,11 +1278,16 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
|
|||
pthread_mutex_lock(&playerMutex);
|
||||
|
||||
if (self->numberOfBuffersUsed == 0
|
||||
&& !disposeWasRequested
|
||||
&& !seekToTimeWasRequested
|
||||
&& self->rebufferingStartFrames == 0
|
||||
&& self.internalState != AudioPlayerInternalStateWaitingForData
|
||||
&& [self moreFramesAreDefinitelyAvailableToPlay])
|
||||
{
|
||||
if ([self moreFramesAreDefinitelyAvailableToPlay])
|
||||
{
|
||||
}
|
||||
|
||||
AudioQueuePause(audioQueue);
|
||||
|
||||
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;
|
||||
|
||||
|
|
@ -1294,6 +1313,8 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
|
|||
}
|
||||
}
|
||||
|
||||
signal = signal || disposeWasRequested;
|
||||
|
||||
if (self.internalState == AudioPlayerInternalStateStopped
|
||||
|| self.internalState == AudioPlayerInternalStateStopping
|
||||
|| self.internalState == AudioPlayerInternalStateDisposed
|
||||
|
|
@ -1368,11 +1389,6 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
|
|||
return NO;
|
||||
}
|
||||
|
||||
if (upcomingQueue.count > 0 && [(STKQueueEntry*)[upcomingQueue peek] isDefinitelyIncompatible:¤tAudioStreamBasicDescription])
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
|
|
@ -1573,7 +1589,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
|
|||
|
||||
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)
|
||||
{
|
||||
|
|
@ -1616,12 +1632,15 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
|
|||
{
|
||||
LOGINFO(@"AudioQueueStop/1");
|
||||
|
||||
pthread_mutex_unlock(&queueBuffersMutex);
|
||||
AudioQueueStop(audioQueue, YES);
|
||||
AudioQueueDispose(audioQueue, YES);
|
||||
|
||||
memset(¤tAudioStreamBasicDescription, 0, sizeof(currentAudioStreamBasicDescription));
|
||||
|
||||
audioQueue = nil;
|
||||
|
||||
pthread_mutex_lock(&queueBuffersMutex);
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
if ([NSThread currentThread] == playbackThread)
|
||||
block = [block copy];
|
||||
|
||||
[self invokeOnPlaybackThread:^
|
||||
{
|
||||
dispatch_sync(dispatch_get_main_queue(), block);
|
||||
}
|
||||
else
|
||||
{
|
||||
[self invokeOnPlaybackThread:^
|
||||
if (disposeWasRequested)
|
||||
{
|
||||
dispatch_sync(dispatch_get_main_queue(), block);
|
||||
}];
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
[self dispatchSyncOnMainThread:block];
|
||||
}];
|
||||
}
|
||||
|
||||
-(BOOL) processRunloop
|
||||
|
|
@ -2175,7 +2231,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
|
|||
|
||||
next = [upcomingQueue peek];
|
||||
|
||||
if ([next couldBeIncompatible:¤tAudioStreamBasicDescription] && currentlyPlayingEntry != nil)
|
||||
if ([next isKnownToBeIncompatible:¤tAudioStreamBasicDescription] && currentlyPlayingEntry != nil)
|
||||
{
|
||||
goto endOfIfElse;
|
||||
}
|
||||
|
|
@ -2618,58 +2674,68 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
|
|||
|
||||
NSObject* queueItemId = currentlyReadingEntry.queueItemId;
|
||||
|
||||
dispatch_sync(dispatch_get_main_queue(), ^
|
||||
if (disposeWasRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
[self dispatchSyncOnMainThread:^
|
||||
{
|
||||
[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;
|
||||
currentlyReadingEntry.lastByteIndex = audioPacketsReadCount;
|
||||
LOGINFO(@"Seeked to end");
|
||||
|
||||
if (audioPacketsReadCount == 0 && audioPacketsPlayedCount == audioPacketsReadCount && currentlyReadingEntry == currentlyPlayingEntry)
|
||||
seekToTimeWasRequested = NO;
|
||||
|
||||
if (audioQueue)
|
||||
{
|
||||
LOGINFO(@"Seeked to end");
|
||||
|
||||
seekToTimeWasRequested = NO;
|
||||
|
||||
if (audioQueue)
|
||||
if ([self audioQueueIsRunning])
|
||||
{
|
||||
if ([self audioQueueIsRunning])
|
||||
{
|
||||
self.internalState = AudioPlayerInternalStateFlushingAndStoppingButStillPlaying;
|
||||
self.internalState = AudioPlayerInternalStateFlushingAndStoppingButStillPlaying;
|
||||
|
||||
LOGINFO(@"Stopping AudioQueue asynchronously");
|
||||
|
||||
if (AudioQueueStop(audioQueue, NO) != 0)
|
||||
{
|
||||
LOGINFO(@"Stopping AudioQueue asynchronously failed");
|
||||
|
||||
[self processFinishedPlayingViaAudioQueueStop];
|
||||
}
|
||||
}
|
||||
else
|
||||
LOGINFO(@"Stopping AudioQueue asynchronously");
|
||||
|
||||
if (AudioQueueStop(audioQueue, NO) != 0)
|
||||
{
|
||||
LOGINFO(@"AudioQueue already stopped");
|
||||
LOGINFO(@"Stopping AudioQueue asynchronously failed");
|
||||
|
||||
[self processFinishedPlayingViaAudioQueueStop];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGINFO(@"AudioQueue already stopped");
|
||||
|
||||
[self processFinishedPlayingViaAudioQueueStop];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OSSpinLockLock(¤tEntryReferencesLock);
|
||||
currentlyReadingEntry = nil;
|
||||
OSSpinLockUnlock(¤tEntryReferencesLock);
|
||||
}
|
||||
else
|
||||
{
|
||||
stopReason = AudioPlayerStopReasonEof;
|
||||
self.internalState = AudioPlayerInternalStateStopped;
|
||||
}
|
||||
OSSpinLockLock(¤tEntryReferencesLock);
|
||||
currentlyReadingEntry = nil;
|
||||
OSSpinLockUnlock(¤tEntryReferencesLock);
|
||||
}
|
||||
else
|
||||
{
|
||||
stopReason = AudioPlayerStopReasonEof;
|
||||
self.internalState = AudioPlayerInternalStateStopped;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&playerMutex);
|
||||
}
|
||||
|
||||
|
|
@ -2786,18 +2852,30 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
|
|||
{
|
||||
BOOL wait = NO;
|
||||
|
||||
pthread_mutex_lock(&playerMutex);
|
||||
NSRunLoop* runLoop = playbackThreadRunLoop;
|
||||
|
||||
if (runLoop != nil)
|
||||
{
|
||||
disposeWasRequested = YES;
|
||||
wait = YES;
|
||||
|
||||
if (playbackThread && playbackThreadRunLoop)
|
||||
[self invokeOnPlaybackThread:^
|
||||
{
|
||||
wait = YES;
|
||||
|
||||
CFRunLoopStop([playbackThreadRunLoop getCFRunLoop]);
|
||||
}
|
||||
pthread_mutex_lock(&playerMutex);
|
||||
disposeWasRequested = YES;
|
||||
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)
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue