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;
|
UISlider* slider;
|
||||||
UISwitch* repeatSwitch;
|
UISwitch* repeatSwitch;
|
||||||
UIButton* playButton;
|
UIButton* playButton;
|
||||||
|
UIButton* disposeButton;
|
||||||
UIButton* playFromHTTPButton;
|
UIButton* playFromHTTPButton;
|
||||||
UIButton* queueShortFileButton;
|
UIButton* queueShortFileButton;
|
||||||
UIButton* playFromLocalFileButton;
|
UIButton* playFromLocalFileButton;
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -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(¤tAudioStreamBasicDescription, &newBasicDescription, sizeof(currentAudioStreamBasicDescription)) != 0)
|
cancel = YES;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (currentlyReadingEntry != currentlyPlayingEntry && audioQueue && currentAudioStreamBasicDescription.mSampleRate != 0)
|
||||||
{
|
{
|
||||||
[currentlyReadingEntry.dataSource unregisterForEvents];
|
if (memcmp(¤tAudioStreamBasicDescription, &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(¤tEntryReferencesLock);
|
|
||||||
currentlyReadingEntry = nil;
|
|
||||||
OSSpinLockUnlock(¤tEntryReferencesLock);
|
|
||||||
|
|
||||||
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(¤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)
|
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:¤tAudioStreamBasicDescription];
|
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)
|
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:¤tAudioStreamBasicDescription])
|
|
||||||
{
|
|
||||||
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(¤tAudioStreamBasicDescription, 0, sizeof(currentAudioStreamBasicDescription));
|
memset(¤tAudioStreamBasicDescription, 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:¤tAudioStreamBasicDescription] && currentlyPlayingEntry != nil)
|
if ([next isKnownToBeIncompatible:¤tAudioStreamBasicDescription] && 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(¤tEntryReferencesLock);
|
OSSpinLockLock(¤tEntryReferencesLock);
|
||||||
currentlyReadingEntry = nil;
|
currentlyReadingEntry = nil;
|
||||||
OSSpinLockUnlock(¤tEntryReferencesLock);
|
OSSpinLockUnlock(¤tEntryReferencesLock);
|
||||||
}
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue