From bd1d3c9aeac77b7dc377ecb94338b2598dd282a5 Mon Sep 17 00:00:00 2001 From: Thong Nguyen Date: Sun, 26 Jan 2014 15:30:16 +0000 Subject: [PATCH] A lot more support and fixups for rebuffering including new AudioPlayerStateBuffering state --- ExampleApp/ExampleApp/AudioPlayerView.h | 1 + ExampleApp/ExampleApp/AudioPlayerView.m | 38 +- StreamingKit/StreamingKit/STKAudioPlayer.h | 14 +- StreamingKit/StreamingKit/STKAudioPlayer.m | 462 ++++++++++++++------- 4 files changed, 349 insertions(+), 166 deletions(-) diff --git a/ExampleApp/ExampleApp/AudioPlayerView.h b/ExampleApp/ExampleApp/AudioPlayerView.h index 41cf7c2..554cc25 100644 --- a/ExampleApp/ExampleApp/AudioPlayerView.h +++ b/ExampleApp/ExampleApp/AudioPlayerView.h @@ -48,6 +48,7 @@ @private NSTimer* timer; UILabel* label; + UILabel* statusLabel; UISlider* slider; UISwitch* repeatSwitch; UIButton* playButton; diff --git a/ExampleApp/ExampleApp/AudioPlayerView.m b/ExampleApp/ExampleApp/AudioPlayerView.m index dacb8ae..4ac7b8e 100644 --- a/ExampleApp/ExampleApp/AudioPlayerView.m +++ b/ExampleApp/ExampleApp/AudioPlayerView.m @@ -71,7 +71,7 @@ [queueShortFileButton setTitle:@"Queue short file" forState:UIControlStateNormal]; playButton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; - playButton.frame = CGRectMake((320 - size.width) / 2, 400, size.width, size.height); + playButton.frame = CGRectMake((320 - size.width) / 2, 380, size.width, size.height); [playButton addTarget:self action:@selector(playButtonPressed) forControlEvents:UIControlEventTouchUpInside]; slider = [[UISlider alloc] initWithFrame:CGRectMake(20, 320, 280, 20)]; @@ -86,6 +86,10 @@ label.textAlignment = NSTextAlignmentCenter; + statusLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, slider.frame.origin.y + slider.frame.size.height + label.frame.size.height + 8, frame.size.width, 50)]; + + statusLabel.textAlignment = NSTextAlignmentCenter; + [self addSubview:slider]; [self addSubview:playButton]; [self addSubview:playFromHTTPButton]; @@ -93,7 +97,8 @@ [self addSubview:queueShortFileButton]; [self addSubview:repeatSwitch]; [self addSubview:label]; - + [self addSubview:statusLabel]; + [self setupTimer]; [self updateControls]; } @@ -122,20 +127,33 @@ -(void) tick { - if (!audioPlayer || audioPlayer.duration == 0) + if (!audioPlayer) { slider.value = 0; label.text = @""; + statusLabel.text = @""; return; } - slider.minimumValue = 0; - slider.maximumValue = audioPlayer.duration; - - slider.value = audioPlayer.progress; + if (audioPlayer.duration != 0) + { + slider.minimumValue = 0; + slider.maximumValue = audioPlayer.duration; + slider.value = audioPlayer.progress; + + label.text = [NSString stringWithFormat:@"%@ - %@", [self formatTimeFromSeconds:audioPlayer.progress], [self formatTimeFromSeconds:audioPlayer.duration]]; + } + else + { + slider.value = 0; + slider.minimumValue = 0; + slider.maximumValue = 0; + + label.text = @""; + } - label.text = [NSString stringWithFormat:@"%@ - %@", [self formatTimeFromSeconds:audioPlayer.progress], [self formatTimeFromSeconds:audioPlayer.duration]]; + statusLabel.text = audioPlayer.state == AudioPlayerStateBuffering ? @"buffering" : @""; } -(void) playFromHTTPButtonTouched @@ -190,7 +208,7 @@ { [playButton setTitle:@"Resume" forState:UIControlStateNormal]; } - else if (audioPlayer.state == AudioPlayerStatePlaying) + else if (audioPlayer.state & AudioPlayerStatePlaying) { [playButton setTitle:@"Pause" forState:UIControlStateNormal]; } @@ -198,6 +216,8 @@ { [playButton setTitle:@"" forState:UIControlStateNormal]; } + + [self tick]; } -(void) setAudioPlayer:(STKAudioPlayer*)value diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.h b/StreamingKit/StreamingKit/STKAudioPlayer.h index f839ebd..327810b 100644 --- a/StreamingKit/StreamingKit/STKAudioPlayer.h +++ b/StreamingKit/StreamingKit/STKAudioPlayer.h @@ -49,8 +49,9 @@ typedef enum AudioPlayerInternalStateInitialised = 0, AudioPlayerInternalStateRunning = 1, AudioPlayerInternalStatePlaying = (1 << 1) | AudioPlayerInternalStateRunning, - AudioPlayerInternalStateStartingThread = (1 << 2) | AudioPlayerInternalStateRunning, - AudioPlayerInternalStateWaitingForData = (1 << 3) | AudioPlayerInternalStateRunning, + AudioPlayerInternalStateRebuffering = (1 << 2) | AudioPlayerInternalStateRunning, + AudioPlayerInternalStateStartingThread = (1 << 3) | AudioPlayerInternalStateRunning, + AudioPlayerInternalStateWaitingForData = (1 << 4) | AudioPlayerInternalStateRunning, AudioPlayerInternalStatePaused = (1 << 5) | AudioPlayerInternalStateRunning, AudioPlayerInternalStateFlushingAndStoppingButStillPlaying = (1 << 7) | AudioPlayerInternalStateRunning, AudioPlayerInternalStateStopping = (1 << 8), @@ -65,10 +66,11 @@ typedef enum AudioPlayerStateReady, AudioPlayerStateRunning = 1, AudioPlayerStatePlaying = (1 << 1) | AudioPlayerStateRunning, - AudioPlayerStatePaused = (1 << 2) | AudioPlayerStateRunning, - AudioPlayerStateStopped = (1 << 3), - AudioPlayerStateError = (1 << 4), - AudioPlayerStateDisposed = (1 << 5) + AudioPlayerStateBuffering = (1 << 2) | AudioPlayerStatePlaying, + AudioPlayerStatePaused = (1 << 3) | AudioPlayerStateRunning, + AudioPlayerStateStopped = (1 << 4), + AudioPlayerStateError = (1 << 5), + AudioPlayerStateDisposed = (1 << 6) } AudioPlayerState; diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.m b/StreamingKit/StreamingKit/STKAudioPlayer.m index 1f8d74b..8d74230 100644 --- a/StreamingKit/StreamingKit/STKAudioPlayer.m +++ b/StreamingKit/StreamingKit/STKAudioPlayer.m @@ -42,11 +42,11 @@ #import "libkern/OSAtomic.h" #define STK_BIT_RATE_ESTIMATION_MIN_PACKETS (64) -#define STK_BUFFERS_NEEDED_TO_START (16) +#define STK_BUFFERS_NEEDED_TO_START (32) #define STK_BUFFERS_NEEDED_WHEN_UNDERUNNING (64) #define STK_DEFAULT_READ_BUFFER_SIZE (2 * 1024) #define STK_DEFAULT_PACKET_BUFFER_SIZE (2048) -#define STK_FRAMES_MISSED_BEFORE_CONSIDERED_UNDERUN (1024) +#define STK_FRAMES_MISSED_BEFORE_CONSIDERED_UNDERRUN (1024) #define STK_DEFAULT_NUMBER_OF_AUDIOQUEUE_BUFFERS (1024) #define OSSTATUS_PARAM_ERROR (-50) @@ -225,7 +225,7 @@ AudioQueueBufferRefLookupEntry; double calculatedBitRate = [self calculatedBitRate]; - if (calculatedBitRate == 0 || self.dataSource.length == 0) + if (calculatedBitRate < 1.0 || self.dataSource.length == 0) { return 0; } @@ -250,6 +250,11 @@ AudioQueueBufferRefLookupEntry; } } +-(BOOL) isDefinitelyIncompatible:(AudioStreamBasicDescription*)basicDescription +{ + return (memcmp(&(self->audioStreamBasicDescription), basicDescription, sizeof(*basicDescription)) != 0); +} + -(BOOL) couldBeIncompatible:(AudioStreamBasicDescription*)basicDescription { if (self->audioStreamBasicDescription.mSampleRate != 0) @@ -318,7 +323,7 @@ AudioQueueBufferRefLookupEntry; AudioPlayerErrorCode errorCode; AudioPlayerStopReason stopReason; - int currentlyPlayingLock; + int currentEntryReferencesLock; pthread_mutex_t playerMutex; pthread_mutex_t queueBuffersMutex; pthread_cond_t queueBufferReadyCondition; @@ -396,6 +401,21 @@ 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) @@ -407,9 +427,9 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ if ([self.delegate respondsToSelector:@selector(audioPlayer:internalStateChanged:)]) { - [self playbackThreadQueueMainThreadSyncBlock:^ + [self syncInvokeOnMainThreadWithPlayerLock: ^ { - [self.delegate audioPlayer:self internalStateChanged:internalState];; + [self.delegate audioPlayer:self internalStateChanged:internalState]; }]; } @@ -422,11 +442,14 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ break; case AudioPlayerInternalStateRunning: case AudioPlayerInternalStateStartingThread: - case AudioPlayerInternalStateWaitingForData: case AudioPlayerInternalStatePlaying: case AudioPlayerInternalStateFlushingAndStoppingButStillPlaying: newState = AudioPlayerStatePlaying; break; + case AudioPlayerInternalStateRebuffering: + case AudioPlayerInternalStateWaitingForData: + newState = AudioPlayerStateBuffering; + break; case AudioPlayerInternalStateStopping: case AudioPlayerInternalStateStopped: newState = AudioPlayerStateStopped; @@ -446,7 +469,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ { self.state = newState; - [self playbackThreadQueueMainThreadSyncBlock:^ + [self syncInvokeOnMainThreadWithPlayerLock: ^ { [self.delegate audioPlayer:self stateChanged:self.state]; }]; @@ -761,66 +784,79 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ } case kAudioFileStreamProperty_DataFormat: { + AudioStreamBasicDescription newBasicDescription; + STKQueueEntry* entryToUpdate = currentlyReadingEntry; + if (currentlyReadingEntry->audioStreamBasicDescription.mSampleRate == 0) { - AudioStreamBasicDescription newBasicDescription; UInt32 size = sizeof(newBasicDescription); AudioFileStreamGetProperty(inAudioFileStream, kAudioFileStreamProperty_DataFormat, &size, &newBasicDescription); - currentlyReadingEntry->audioStreamBasicDescription = newBasicDescription; - } + + pthread_mutex_lock(&playerMutex); - currentlyReadingEntry->sampleRate = currentlyReadingEntry->audioStreamBasicDescription.mSampleRate; - currentlyReadingEntry->packetDuration = currentlyReadingEntry->audioStreamBasicDescription.mFramesPerPacket / currentlyReadingEntry->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); + BOOL cancel = NO; - if (error || packetBufferSize == 0) + AudioQueueSetParameter(audioQueue, kAudioQueueParam_Volume, 1); + + if (currentlyReadingEntry != currentlyPlayingEntry && audioQueue && currentAudioStreamBasicDescription.mSampleRate != 0) { - currentlyReadingEntry->packetBufferSize = STK_DEFAULT_PACKET_BUFFER_SIZE; - } - else - { - currentlyReadingEntry->packetBufferSize = packetBufferSize; - } - } - else - { - currentlyReadingEntry->packetBufferSize = packetBufferSize; - } - - [currentlyReadingEntry updateAudioDataSource]; - - AudioQueueSetParameter(audioQueue, kAudioQueueParam_Volume, 1); - - if (currentlyReadingEntry != currentlyPlayingEntry && audioQueue && currentAudioStreamBasicDescription.mSampleRate != 0) - { - if (memcmp(¤tAudioStreamBasicDescription, ¤tlyReadingEntry->audioStreamBasicDescription, sizeof(currentAudioStreamBasicDescription)) != 0) - { - [currentlyReadingEntry.dataSource unregisterForEvents]; - - if ([bufferingQueue objectAtIndex:0] == currentlyReadingEntry) + if (memcmp(¤tAudioStreamBasicDescription, &newBasicDescription, sizeof(currentAudioStreamBasicDescription)) != 0) { - [bufferingQueue removeObjectAtIndex:0]; + [currentlyReadingEntry.dataSource unregisterForEvents]; + + 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); + + cancel = YES; + } + } + + if (!cancel) + { + 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) + { + entryToUpdate->packetBufferSize = STK_DEFAULT_PACKET_BUFFER_SIZE; + } + else + { + entryToUpdate->packetBufferSize = packetBufferSize; + } + } + else + { + entryToUpdate->packetBufferSize = packetBufferSize; } - STKQueueEntry* newEntry = [[STKQueueEntry alloc] initWithDataSource:currentlyReadingEntry.dataSource andQueueItemId:currentlyReadingEntry.queueItemId]; - - newEntry->audioStreamBasicDescription = currentlyReadingEntry->audioStreamBasicDescription; - - [upcomingQueue skipQueue:newEntry]; - - OSSpinLockLock(¤tlyPlayingLock); - currentlyReadingEntry = nil; - OSSpinLockUnlock(¤tlyPlayingLock); + [entryToUpdate updateAudioDataSource]; } + + pthread_mutex_unlock(&playerMutex); } break; @@ -1072,6 +1108,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ } } + -(BOOL) processFinishedPlaying:(STKQueueEntry*)entry { LOGINFO(entry.queueItemId); @@ -1097,7 +1134,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ if (currentlyPlayingEntry) { - OSSpinLockLock(¤tlyPlayingLock); + OSSpinLockLock(¤tEntryReferencesLock); { if (currentlyPlayingEntry) { @@ -1109,7 +1146,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ } } } - OSSpinLockUnlock(¤tlyPlayingLock); + OSSpinLockUnlock(¤tEntryReferencesLock); } int index = (int)bufferIn % audioQueueBufferRefLookupCount; @@ -1197,7 +1234,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ { self.internalState = AudioPlayerInternalStateFlushingAndStoppingButStillPlaying; - LOGINFO(@"AudioQueueStop from handleAudioQUeueOutput"); + LOGINFO(@"AudioQueueStop from handleAudioQueueOutput"); AudioQueueStop(audioQueue, NO); } @@ -1211,28 +1248,43 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ if (!audioQueueFlushing) { - if (numberOfBuffersUsed == 0 && !seekToTimeWasRequested) + if (numberOfBuffersUsed == 0 + && !seekToTimeWasRequested + && self.internalState != AudioPlayerInternalStateWaitingForData) { - OSSpinLockLock(¤tlyPlayingLock); + LOGINFO(([NSString stringWithFormat:@"UNDERRUN WITH TIME: %f", [self currentTimeInFrames]])); - Float64 time = [self currentTimeInFrames]; - - if (self->rebufferingStartFrames == 0 - && time >= STK_FRAMES_MISSED_BEFORE_CONSIDERED_UNDERUN - && self.internalState != AudioPlayerInternalStateWaitingForData - && currentlyReadingEntry != nil) + if (self->rebufferingStartFrames == 0) { - LOGINFO(@"Buffer underun"); - - AudioQueuePause(audioQueue); - - self->rebufferingStartFrames = [self currentTimeInFrames]; - - OSSpinLockUnlock(¤tlyPlayingLock); + [self invokeOnPlaybackThread:^ + { + pthread_mutex_lock(&playerMutex); + + if (self->numberOfBuffersUsed == 0 + && !seekToTimeWasRequested + && self->rebufferingStartFrames == 0 + && self.internalState != AudioPlayerInternalStateWaitingForData + && [self moreFramesAreDefinitelyAvailableToPlay]) + { + AudioQueuePause(audioQueue); + + self->rebufferingStartFrames = [self currentTimeInFrames]; + + LOGINFO(([NSString stringWithFormat:@"Buffer underrun with time: %f", [self currentTimeInFrames]])); + } + + pthread_mutex_unlock(&playerMutex); + }]; } - else + + if (self.internalState != AudioPlayerStateBuffering && self.internalState != AudioPlayerInternalStatePaused) { - OSSpinLockUnlock(¤tlyPlayingLock); + Float64 interval = STK_FRAMES_MISSED_BEFORE_CONSIDERED_UNDERRUN / currentAudioStreamBasicDescription.mSampleRate; + + [self invokeOnPlaybackThreadAtInterval:interval withBlock:^ + { + [self setRebufferingStateIfApplicable]; + }]; } signal = YES; @@ -1252,18 +1304,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ } else { - if (seekToTimeWasRequested) - { - signal = YES; - } - else - { -// if ((waiting && numberOfBuffersUsed < audioQueueBufferCount / 2) || (numberOfBuffersUsed < (STK_BUFFERS_NEEDED_TO_START * 2))) - if (numberOfBuffersUsed < (STK_BUFFERS_NEEDED_TO_START * 2)) - { - signal = YES; - } - } + signal = signal || seekToTimeWasRequested || (numberOfBuffersUsed < self->audioQueueBufferCount / 2); } if (signal) @@ -1274,6 +1315,64 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ pthread_mutex_unlock(&queueBuffersMutex); } +-(void) setRebufferingStateIfApplicable +{ + pthread_mutex_lock(&playerMutex); + + if (self->rebufferingStartFrames > 0) + { + if ([self currentTimeInFrames] > STK_FRAMES_MISSED_BEFORE_CONSIDERED_UNDERRUN + && self.internalState != AudioPlayerStateBuffering + && self.internalState != AudioPlayerInternalStatePaused) + { + self.internalState = AudioPlayerInternalStateRebuffering; + } + else + { + Float64 interval = STK_FRAMES_MISSED_BEFORE_CONSIDERED_UNDERRUN / currentAudioStreamBasicDescription.mSampleRate / 2; + + [self invokeOnPlaybackThreadAtInterval:interval withBlock:^ + { + [self setRebufferingStateIfApplicable]; + }]; + } + } + + pthread_mutex_unlock(&playerMutex); +} + +-(BOOL) moreFramesAreDefinitelyAvailableToPlay +{ + if (currentlyReadingEntry == currentlyPlayingEntry) + { + if (currentlyReadingEntry.dataSource.position == currentlyReadingEntry.dataSource.length) + { + return NO; + } + else + { + return YES; + } + } + + if (bufferingQueue.count > 1) + { + return YES; + } + + if (bufferingQueue.count == 1 && ((STKQueueEntry*)[bufferingQueue peek])->audioStreamBasicDescription.mSampleRate == 0) + { + return NO; + } + + if (upcomingQueue.count > 0 && [(STKQueueEntry*)[upcomingQueue peek] isDefinitelyIncompatible:¤tAudioStreamBasicDescription]) + { + return YES; + } + + return NO; +} + -(Float64) currentTimeInFrames { if (audioQueue == nil) @@ -1343,6 +1442,20 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ } } +-(BOOL) readyToEndRebufferingState +{ + BOOL tailEndOfBuffer = ![self moreFramesAreDefinitelyAvailableToPlay]; + + return self->rebufferingStartFrames > 0 && (numberOfBuffersUsed > STK_BUFFERS_NEEDED_WHEN_UNDERUNNING || tailEndOfBuffer); +} + +-(BOOL) readyToEndWaitingForDataState +{ + BOOL tailEndOfBuffer = ![self moreFramesAreDefinitelyAvailableToPlay]; + + return self.internalState == AudioPlayerInternalStateWaitingForData && (numberOfBuffersUsed >= STK_BUFFERS_NEEDED_TO_START || tailEndOfBuffer); +} + -(void) enqueueBuffer { pthread_mutex_lock(&playerMutex); @@ -1393,7 +1506,6 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ pthread_mutex_unlock(&queueBuffersMutex); - if (error) { [self stopAudioQueueWithReason:@"enqueueBuffer critical error"]; @@ -1403,35 +1515,43 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ return; } - OSSpinLockLock(¤tlyPlayingLock); + OSSpinLockLock(¤tEntryReferencesLock); - BOOL tailEndOfBuffer = currentlyReadingEntry == currentlyPlayingEntry && currentlyReadingEntry.dataSource.position == currentlyReadingEntry.dataSource.length; - - if (self->rebufferingStartFrames > 0 && (numberOfBuffersUsed > STK_BUFFERS_NEEDED_WHEN_UNDERUNNING || tailEndOfBuffer)) + if ([self readyToEndRebufferingState]) { - self->rebufferingStartFrames = 0; + OSSpinLockUnlock(¤tEntryReferencesLock); - OSSpinLockUnlock(¤tlyPlayingLock); - - if (![self startAudioQueue] || audioQueue == nil) + if (self.internalState != AudioPlayerInternalStatePaused) { - pthread_mutex_unlock(&playerMutex); + self->rebufferingStartFrames = 0; - return; + if (![self startAudioQueue] || audioQueue == nil) + { + pthread_mutex_unlock(&playerMutex); + + return; + } + } + else + { + OSSpinLockUnlock(¤tEntryReferencesLock); } } else { - OSSpinLockUnlock(¤tlyPlayingLock); + OSSpinLockUnlock(¤tEntryReferencesLock); } - if (self.internalState == AudioPlayerInternalStateWaitingForData && (numberOfBuffersUsed >= STK_BUFFERS_NEEDED_TO_START || tailEndOfBuffer)) + if ([self readyToEndWaitingForDataState]) { - if (![self startAudioQueue] || audioQueue == nil) + if (self.internalState != AudioPlayerInternalStatePaused) { - pthread_mutex_unlock(&playerMutex); - - return; + if (![self startAudioQueue] || audioQueue == nil) + { + pthread_mutex_unlock(&playerMutex); + + return; + } } } @@ -1658,20 +1778,20 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ return 0; } - OSSpinLockLock(¤tlyPlayingLock); + OSSpinLockLock(¤tEntryReferencesLock); STKQueueEntry* entry = currentlyPlayingEntry; if (entry == nil) { - OSSpinLockUnlock(¤tlyPlayingLock); + OSSpinLockUnlock(¤tEntryReferencesLock); return 0; } double retval = [entry duration]; - OSSpinLockUnlock(¤tlyPlayingLock); + OSSpinLockUnlock(¤tEntryReferencesLock); return retval; } @@ -1688,14 +1808,14 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ return 0; } - OSSpinLockLock(¤tlyPlayingLock); + OSSpinLockLock(¤tEntryReferencesLock); Float64 currentTime = [self currentTimeInFrames]; STKQueueEntry* entry = currentlyPlayingEntry; if (entry == nil) { - OSSpinLockUnlock(¤tlyPlayingLock); + OSSpinLockUnlock(¤tEntryReferencesLock); return 0; } @@ -1704,7 +1824,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ double retval = [entry calculateProgressWithTotalFramesPlayed:time]; - OSSpinLockUnlock(¤tlyPlayingLock); + OSSpinLockUnlock(¤tEntryReferencesLock); return retval; } @@ -1715,7 +1835,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ if (runLoop) { - CFRunLoopPerformBlock([runLoop getCFRunLoop], NSDefaultRunLoopMode, block); + CFRunLoopPerformBlock([runLoop getCFRunLoop], NSRunLoopCommonModes, block); CFRunLoopWakeUp([runLoop getCFRunLoop]); return YES; @@ -1724,6 +1844,29 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ return NO; } +-(void)invokeOnPlaybackThreadAtIntervalHelper:(NSTimer*)timer +{ + void(^block)() = (void(^)())timer.userInfo; + + block(); +} + +-(BOOL) invokeOnPlaybackThreadAtInterval:(NSTimeInterval)interval withBlock:(void(^)())block +{ + NSRunLoop* runLoop = playbackThreadRunLoop; + + if (runLoop) + { + NSTimer* timer = [NSTimer timerWithTimeInterval:interval target:self selector:@selector(invokeOnPlaybackThreadAtIntervalHelper:) userInfo:[block copy] repeats:NO]; + + [runLoop addTimer:timer forMode:NSRunLoopCommonModes]; + + return YES; + } + + return NO; +} + -(void) wakeupPlaybackThread { [self invokeOnPlaybackThread:^ @@ -1739,7 +1882,6 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ } pthread_mutex_unlock(&queueBuffersMutex); - } -(void) seekToTime:(double)value @@ -1813,9 +1955,14 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ [currentlyReadingEntry.dataSource close]; } + OSSpinLockLock(¤tEntryReferencesLock); currentlyReadingEntry = entry; + OSSpinLockUnlock(¤tEntryReferencesLock); + currentlyReadingEntry.dataSource.delegate = self; + + [currentlyReadingEntry.dataSource registerForEvents:[NSRunLoop currentRunLoop]]; [currentlyReadingEntry.dataSource seekToOffset:0]; @@ -1877,12 +2024,12 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ seekToTimeWasRequested = NO; } - OSSpinLockLock(¤tlyPlayingLock); + OSSpinLockLock(¤tEntryReferencesLock); currentlyPlayingEntry = next; currentlyPlayingEntry.bytesBuffered = 0; currentlyPlayingEntry.firstFrameIndex = [self currentTimeInFrames]; NSObject* playingQueueItemId = playingQueueItemId = currentlyPlayingEntry.queueItemId; - OSSpinLockUnlock(¤tlyPlayingLock); + OSSpinLockUnlock(¤tEntryReferencesLock); if (!isPlayingSameItemProbablySeek && entry) { @@ -1902,9 +2049,9 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ } else { - OSSpinLockLock(¤tlyPlayingLock); + OSSpinLockLock(¤tEntryReferencesLock); currentlyPlayingEntry = nil; - OSSpinLockUnlock(¤tlyPlayingLock); + OSSpinLockUnlock(¤tEntryReferencesLock); if (currentlyReadingEntry == nil) { @@ -1927,10 +2074,17 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ -(void) playbackThreadQueueMainThreadSyncBlock:(void(^)())block { - [self invokeOnPlaybackThread:^ + if ([NSThread currentThread] == playbackThread) { dispatch_sync(dispatch_get_main_queue(), block); - }]; + } + else + { + [self invokeOnPlaybackThread:^ + { + dispatch_sync(dispatch_get_main_queue(), block); + }]; + } } -(BOOL) processRunloop @@ -1985,13 +2139,12 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ [bufferingQueue dequeue]; } - OSSpinLockLock(¤tlyPlayingLock); + OSSpinLockLock(¤tEntryReferencesLock); currentlyPlayingEntry = nil; - OSSpinLockUnlock(¤tlyPlayingLock); - currentlyReadingEntry = nil; seekToTimeWasRequested = NO; - } + OSSpinLockUnlock(¤tEntryReferencesLock); + } else if (self.internalState == AudioPlayerInternalStateStopped && stopReason == AudioPlayerStopReasonUserActionFlushStop) { currentlyReadingEntry.dataSource.delegate = nil; @@ -2008,11 +2161,10 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ [bufferingQueue dequeue]; } - OSSpinLockLock(¤tlyPlayingLock); + OSSpinLockLock(¤tEntryReferencesLock); currentlyPlayingEntry = nil; - OSSpinLockUnlock(¤tlyPlayingLock); - currentlyReadingEntry = nil; + OSSpinLockUnlock(¤tEntryReferencesLock); [self resetAudioQueueWithReason:@"from processRunLoop"]; } @@ -2091,7 +2243,6 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ @autoreleasepool { playbackThreadRunLoop = [NSRunLoop currentRunLoop]; - NSThread.currentThread.threadPriority = 1; bytesFilled = 0; @@ -2106,7 +2257,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ break; } - NSDate *date = [[NSDate alloc] initWithTimeIntervalSinceNow:10]; + NSDate* date = [[NSDate alloc] initWithTimeIntervalSinceNow:10]; [playbackThreadRunLoop runMode:NSDefaultRunLoopMode beforeDate:date]; } @@ -2116,12 +2267,11 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ currentlyReadingEntry.dataSource.delegate = nil; currentlyPlayingEntry.dataSource.delegate = nil; - currentlyReadingEntry = nil; - pthread_mutex_lock(&playerMutex); - OSSpinLockLock(¤tlyPlayingLock); + OSSpinLockLock(¤tEntryReferencesLock); currentlyPlayingEntry = nil; - OSSpinLockUnlock(¤tlyPlayingLock); + currentlyReadingEntry = nil; + OSSpinLockUnlock(¤tEntryReferencesLock); pthread_mutex_unlock(&playerMutex); self.internalState = AudioPlayerInternalStateDisposed; @@ -2134,9 +2284,9 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ -(void) processSeekToTime { OSStatus error; - OSSpinLockLock(¤tlyPlayingLock); + OSSpinLockLock(¤tEntryReferencesLock); STKQueueEntry* currentEntry = currentlyReadingEntry; - OSSpinLockUnlock(¤tlyPlayingLock); + OSSpinLockUnlock(¤tEntryReferencesLock); NSAssert(currentEntry == currentlyPlayingEntry, @"playing and reading must be the same"); @@ -2306,7 +2456,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ { error = AudioQueueReset(audioQueue); - OSSpinLockLock(¤tlyPlayingLock); + OSSpinLockLock(¤tEntryReferencesLock); AudioTimeStamp timeStamp; Boolean outTimelineDiscontinuity; @@ -2323,7 +2473,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ rebufferingStartFrames = 0; } - OSSpinLockUnlock(¤tlyPlayingLock); + OSSpinLockUnlock(¤tEntryReferencesLock); if (startAudioQueue) { @@ -2434,7 +2584,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ return; } - OSSpinLockLock(¤tlyPlayingLock); + OSSpinLockLock(¤tEntryReferencesLock); if (currentlyReadingEntry == nil) { @@ -2442,7 +2592,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ [dataSourceIn close]; } - OSSpinLockUnlock(¤tlyPlayingLock); + OSSpinLockUnlock(¤tEntryReferencesLock); } } @@ -2513,8 +2663,10 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ } } } - + + OSSpinLockLock(¤tEntryReferencesLock); currentlyReadingEntry = nil; + OSSpinLockUnlock(¤tEntryReferencesLock); } else { @@ -2570,16 +2722,24 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ { [self resetAudioQueueWithReason:@"from resume"]; } - - error = AudioQueueStart(audioQueue, 0); - - if (error) + + if (audioQueue != nil) { - [self didEncounterError:AudioPlayerErrorQueueStartFailed]; - - pthread_mutex_unlock(&playerMutex); - - return; + if(!((self.internalState == AudioPlayerInternalStateWaitingForData) || (self.internalState == AudioPlayerInternalStateRebuffering)) + || [self readyToEndRebufferingState] + || [self readyToEndWaitingForDataState]) + { + error = AudioQueueStart(audioQueue, 0); + + if (error) + { + [self didEncounterError:AudioPlayerErrorQueueStartFailed]; + + pthread_mutex_unlock(&playerMutex); + + return; + } + } } [self wakeupPlaybackThread]; @@ -2668,20 +2828,20 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ -(NSObject*) currentlyPlayingQueueItemId { - OSSpinLockLock(¤tlyPlayingLock); + OSSpinLockLock(¤tEntryReferencesLock); STKQueueEntry* entry = currentlyPlayingEntry; if (entry == nil) { - OSSpinLockUnlock(¤tlyPlayingLock); + OSSpinLockUnlock(¤tEntryReferencesLock); return nil; } NSObject* retval = entry.queueItemId; - OSSpinLockUnlock(¤tlyPlayingLock); + OSSpinLockUnlock(¤tEntryReferencesLock); return retval; }