From b15766ac18934383c57a21b93f5f89c4ff843efc Mon Sep 17 00:00:00 2001 From: Thong Nguyen Date: Sat, 24 Nov 2012 14:00:16 +0000 Subject: [PATCH] Battery life optimisations on iOS. Increased background plyback battery life by 200-300% --- .../UserInterfaceState.xcuserstate | Bin 11762 -> 13222 bytes Audjustable/Classes/AudioPlayer/AudioPlayer.h | 22 +- Audjustable/Classes/AudioPlayer/AudioPlayer.m | 458 +++++++++++------- .../AutoRecoveringHttpDataSource.m | 3 + Audjustable/Classes/AudioPlayer/DataSource.h | 5 + 5 files changed, 315 insertions(+), 173 deletions(-) diff --git a/Audjustable.xcodeproj/project.xcworkspace/xcuserdata/tum.xcuserdatad/UserInterfaceState.xcuserstate b/Audjustable.xcodeproj/project.xcworkspace/xcuserdata/tum.xcuserdatad/UserInterfaceState.xcuserstate index c8f9c0de6a0a8899cda91afdc5aff07bf3d0e38e..a8c1c95079b9d0470a634f7a7ad558448e42bcae 100644 GIT binary patch delta 7879 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 EAFpd?%>V!Z delta 6854 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 C8WC*( diff --git a/Audjustable/Classes/AudioPlayer/AudioPlayer.h b/Audjustable/Classes/AudioPlayer/AudioPlayer.h index b99a8dd..023a912 100644 --- a/Audjustable/Classes/AudioPlayer/AudioPlayer.h +++ b/Audjustable/Classes/AudioPlayer/AudioPlayer.h @@ -5,7 +5,7 @@ https://github.com/tumtumtum/audjustable Inspired by Matt Gallagher's AudioStreamer: - https://github.com/mattgallagher/AudioStreamer + https://github.com/mattgallagher/AudioStreamer Copyright (c) 2012 Thong Nguyen (tumtumtum@gmail.com). All rights reserved. @@ -33,14 +33,14 @@ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -**********************************************************************************/ + **********************************************************************************/ #import #import #import "DataSource.h" #include -#define AudioPlayerDefaultNumberOfAudioQueueBuffers (8 * 1024) +#define AudioPlayerDefaultNumberOfAudioQueueBuffers (2 * 1024) typedef enum { @@ -121,8 +121,8 @@ AudioQueueBufferRefLookupEntry; UInt8* readBuffer; int readBufferSize; - dispatch_queue_t fastApiSerialQueue; - + NSOperationQueue* fastApiQueue; + QueueEntry* currentlyPlayingEntry; QueueEntry* currentlyReadingEntry; @@ -143,9 +143,9 @@ AudioQueueBufferRefLookupEntry; NSThread* playbackThread; NSRunLoop* playbackThreadRunLoop; NSConditionLock* threadFinishedCondLock; - + AudioFileStreamID audioFileStream; - + BOOL discontinuous; BOOL nextIsIncompatible; @@ -153,20 +153,22 @@ AudioQueueBufferRefLookupEntry; int packetsFilled; int fillBufferIndex; - + UIBackgroundTaskIdentifier backgroundTaskId; AudioPlayerErrorCode errorCode; AudioPlayerStopReason stopReason; + pthread_mutex_t playerMutex; pthread_mutex_t queueBuffersMutex; pthread_cond_t queueBufferReadyCondition; + volatile BOOL waiting; volatile BOOL disposeWasRequested; volatile BOOL seekToTimeWasRequested; volatile BOOL newFileToPlay; - volatile double requestedSeekTime; - volatile BOOL audioQueueFlushing; + volatile double requestedSeekTime; + volatile BOOL audioQueueFlushing; volatile SInt64 audioPacketsReadCount; volatile SInt64 audioPacketsPlayedCount; } diff --git a/Audjustable/Classes/AudioPlayer/AudioPlayer.m b/Audjustable/Classes/AudioPlayer/AudioPlayer.m index 5c71db3..0b60148 100644 --- a/Audjustable/Classes/AudioPlayer/AudioPlayer.m +++ b/Audjustable/Classes/AudioPlayer/AudioPlayer.m @@ -5,7 +5,7 @@ https://github.com/tumtumtum/audjustable Inspired by Matt Gallagher's AudioStreamer: - https://github.com/mattgallagher/AudioStreamer + https://github.com/mattgallagher/AudioStreamer Copyright (c) 2012 Thong Nguyen (tumtumtum@gmail.com). All rights reserved. @@ -33,7 +33,7 @@ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -**********************************************************************************/ + **********************************************************************************/ #import "AudioPlayer.h" #import "AudioToolbox/AudioToolbox.h" @@ -43,9 +43,17 @@ #define BitRateEstimationMinPackets (64) #define AudioPlayerBuffersNeededToStart (16) -#define AudioPlayerDefaultReadBufferSize (4 * 1024) +#define AudioPlayerDefaultReadBufferSize (32 * 1024) #define AudioPlayerDefaultPacketBufferSize (1024) +#define OSSTATUS_PARAM_ERROR (-50) + +#define SPIN_LOCK_LOCK(x) \ + while(OSAtomicCompareAndSwapInt(0, 1, x) == false); + +#define SPIN_LOCK_UNLOCK(x) \ + *x = 0; + @interface NSMutableArray(AudioPlayerExtensions) -(void) enqueue:(id)obj; -(id) dequeue; @@ -163,6 +171,19 @@ return retval; } +-(void) updateAudioDataSource +{ + if ([self->dataSource conformsToProtocol:@protocol(AudioDataSource)]) + { + double calculatedBitrate = [self calculatedBitRate]; + + id audioDataSource = (id)self->dataSource; + + audioDataSource.averageBitRate = calculatedBitrate; + audioDataSource.audioDataOffset = audioDataOffset; + } +} + -(double) progress { double retval = lastProgress; @@ -175,6 +196,8 @@ retval = self->bytesPlayed / calculatedBitrate * 8; retval = seekTime + retval; + + [self updateAudioDataSource]; } if (retval > duration) @@ -251,7 +274,7 @@ @end static void AudioFileStreamPropertyListenerProc(void* clientData, AudioFileStreamID audioFileStream, AudioFileStreamPropertyID propertyId, UInt32* flags) -{ +{ AudioPlayer* player = (__bridge AudioPlayer*)clientData; [player handlePropertyChangeForFileStream:audioFileStream fileStreamPropertyID:propertyId ioFlags:flags]; @@ -298,11 +321,11 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ if ([self.delegate respondsToSelector:@selector(internalStateChanged:)]) { dispatch_async(dispatch_get_main_queue(), ^ - { - [self.delegate audioPlayer:self internalStateChanged:internalState]; - }); + { + [self.delegate audioPlayer:self internalStateChanged:internalState]; + }); } - + AudioPlayerState newState; switch (internalState) @@ -335,11 +358,11 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ if (newState != self.state) { self.state = newState; - + dispatch_async(dispatch_get_main_queue(), ^ - { - [self.delegate audioPlayer:self stateChanged:self.state]; - }); + { + [self.delegate audioPlayer:self stateChanged:self.state]; + }); } } @@ -367,23 +390,28 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ { if (self = [super init]) { - fastApiSerialQueue = dispatch_queue_create("AudioPlayer.fastepi", 0); - + fastApiQueue = [[NSOperationQueue alloc] init]; + [fastApiQueue setMaxConcurrentOperationCount:1]; + readBufferSize = readBufferSizeIn; readBuffer = calloc(sizeof(UInt8), readBufferSize); audioQueueBufferCount = numberOfAudioQueueBuffers; audioQueueBuffer = calloc(sizeof(AudioQueueBufferRef), audioQueueBufferCount); - audioQueueBufferRefLookupCount = audioQueueBufferCount * 2; + audioQueueBufferRefLookupCount = audioQueueBufferCount * 2; audioQueueBufferLookup = calloc(sizeof(AudioQueueBufferRefLookupEntry), audioQueueBufferRefLookupCount); packetDescs = calloc(sizeof(AudioStreamPacketDescription), audioQueueBufferCount); bufferUsed = calloc(sizeof(bool), audioQueueBufferCount); + pthread_mutexattr_t attr; + + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&playerMutex, &attr); pthread_mutex_init(&queueBuffersMutex, NULL); pthread_cond_init(&queueBufferReadyCondition, NULL); - + threadFinishedCondLock = [[NSConditionLock alloc] initWithCondition:0]; self.internalState = AudioPlayerInternalStateInitialised; @@ -397,8 +425,17 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ -(void) dealloc { - dispatch_release(fastApiSerialQueue); - + if (currentlyReadingEntry) + { + currentlyReadingEntry.dataSource.delegate = nil; + } + + if (currentlyPlayingEntry) + { + currentlyPlayingEntry.dataSource.delegate = nil; + } + + pthread_mutex_destroy(&playerMutex); pthread_mutex_destroy(&queueBuffersMutex); pthread_cond_destroy(&queueBufferReadyCondition); @@ -421,31 +458,35 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ -(void) startSystemBackgroundTask { - @synchronized(self) + pthread_mutex_lock(&playerMutex); { if (backgroundTaskId != UIBackgroundTaskInvalid) { + pthread_mutex_unlock(&playerMutex); + return; } backgroundTaskId = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^ - { - [self stopSystemBackgroundTask]; - }]; + { + [self stopSystemBackgroundTask]; + }]; } + pthread_mutex_unlock(&playerMutex); } -(void) stopSystemBackgroundTask { - @synchronized(self) + pthread_mutex_lock(&playerMutex); { - if (backgroundTaskId != UIBackgroundTaskInvalid) + if (backgroundTaskId != UIBackgroundTaskInvalid) { [[UIApplication sharedApplication] endBackgroundTask:backgroundTaskId]; backgroundTaskId = UIBackgroundTaskInvalid; } } + pthread_mutex_unlock(&playerMutex); } -(DataSource*) dataSourceFromURL:(NSURL*)url @@ -460,13 +501,13 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ { retval = [[HttpDataSource alloc] initWithURL:url]; } - + return retval; } -(void) clearQueue { - @synchronized(self) + pthread_mutex_lock(&playerMutex); { NSMutableArray* array = [[NSMutableArray alloc] initWithCapacity:bufferingQueue.count + upcomingQueue.count]; @@ -497,6 +538,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ } }); } + pthread_mutex_unlock(&playerMutex); } -(void) play:(NSURL*)url @@ -506,39 +548,43 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ -(void) setDataSource:(DataSource*)dataSourceIn withQueueItemId:(NSObject*)queueItemId { - dispatch_async(fastApiSerialQueue, ^ - { - @synchronized(self) - { - [self startSystemBackgroundTask]; - - [self clearQueue]; - - [upcomingQueue enqueue:[[QueueEntry alloc] initWithDataSource:dataSourceIn andQueueItemId:queueItemId]]; - - self.internalState = AudioPlayerInternalStateInitialised; - [self processQueue:YES]; - } - }); + [fastApiQueue cancelAllOperations]; + + [fastApiQueue addOperationWithBlock:^ + { + pthread_mutex_lock(&playerMutex); + { + [self startSystemBackgroundTask]; + + [self clearQueue]; + + [upcomingQueue enqueue:[[QueueEntry alloc] initWithDataSource:dataSourceIn andQueueItemId:queueItemId]]; + + self.internalState = AudioPlayerInternalStateRunning; + [self processQueue:YES]; + } + pthread_mutex_unlock(&playerMutex); + }]; } -(void) queueDataSource:(DataSource*)dataSourceIn withQueueItemId:(NSObject*)queueItemId { - dispatch_async(fastApiSerialQueue, ^ - { - @synchronized(self) - { - [upcomingQueue enqueue:[[QueueEntry alloc] initWithDataSource:dataSourceIn andQueueItemId:queueItemId]]; - - [self processQueue:NO]; - } - }); + [fastApiQueue addOperationWithBlock:^ + { + pthread_mutex_lock(&playerMutex); + { + [upcomingQueue enqueue:[[QueueEntry alloc] initWithDataSource:dataSourceIn andQueueItemId:queueItemId]]; + + [self processQueue:NO]; + } + pthread_mutex_unlock(&playerMutex); + }]; } -(void) handlePropertyChangeForFileStream:(AudioFileStreamID)inAudioFileStream fileStreamPropertyID:(AudioFileStreamPropertyID)inPropertyID ioFlags:(UInt32*)ioFlags { OSStatus error; - + switch (inPropertyID) { case kAudioFileStreamProperty_DataOffset: @@ -550,13 +596,15 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ currentlyReadingEntry->parsedHeader = YES; currentlyReadingEntry->audioDataOffset = offset; + + [currentlyReadingEntry updateAudioDataSource]; } - break; + break; case kAudioFileStreamProperty_DataFormat: { AudioStreamBasicDescription newBasicDescription; UInt32 size = sizeof(newBasicDescription); - + AudioFileStreamGetProperty(inAudioFileStream, kAudioFileStreamProperty_DataFormat, &size, &newBasicDescription); currentlyReadingEntry->audioStreamBasicDescription = newBasicDescription; @@ -577,9 +625,13 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ { currentlyReadingEntry->packetBufferSize = AudioPlayerDefaultPacketBufferSize; } - } + } + + [currentlyReadingEntry updateAudioDataSource]; + + AudioQueueSetParameter(audioQueue, kAudioQueueParam_Volume, 1); } - break; + break; case kAudioFileStreamProperty_AudioDataByteCount: { UInt64 audioDataByteCount; @@ -588,19 +640,26 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ AudioFileStreamGetProperty(inAudioFileStream, kAudioFileStreamProperty_AudioDataByteCount, &byteCountSize, &audioDataByteCount); currentlyReadingEntry->audioDataByteCount = audioDataByteCount; + + [currentlyReadingEntry updateAudioDataSource]; } - break; + break; case kAudioFileStreamProperty_ReadyToProducePackets: { discontinuous = YES; } - break; + break; } } -(void) handleAudioPackets:(const void*)inputData numberBytes:(UInt32)numberBytes numberPackets:(UInt32)numberPackets packetDescriptions:(AudioStreamPacketDescription*)packetDescriptionsIn { if (currentlyReadingEntry == nil) + { + return; + } + + if (seekToTimeWasRequested) { return; } @@ -624,7 +683,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ SInt64 packetOffset = packetDescriptionsIn[i].mStartOffset; SInt64 packetSize = packetDescriptionsIn[i].mDataByteSize; int bufSpaceRemaining; - + if (currentlyReadingEntry->processedPacketsSizeTotal < 0xfffff) { OSAtomicAdd32(packetSize, ¤tlyReadingEntry->processedPacketsSizeTotal); @@ -636,11 +695,16 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ return; } - bufSpaceRemaining = currentlyReadingEntry->packetBufferSize - bytesFilled; + bufSpaceRemaining = currentlyReadingEntry->packetBufferSize - bytesFilled; if (bufSpaceRemaining < packetSize) { [self enqueueBuffer]; + + if (seekToTimeWasRequested) + { + return; + } } if (bytesFilled + packetSize > currentlyReadingEntry->packetBufferSize) @@ -665,7 +729,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ } } } - else + else { // CBR @@ -679,12 +743,12 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ { [self enqueueBuffer]; } - - @synchronized(self) + + pthread_mutex_lock(&playerMutex); { int copySize; bytesLeft = AudioPlayerDefaultPacketBufferSize - bytesFilled; - + if (bytesLeft < numberBytes) { copySize = bytesLeft; @@ -693,9 +757,11 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ { copySize = numberBytes; } - + if (bytesFilled > currentlyPlayingEntry->packetBufferSize) { + pthread_mutex_unlock(&playerMutex); + return; } @@ -707,6 +773,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ numberBytes -= copySize; offset += copySize; } + pthread_mutex_unlock(&playerMutex); } } } @@ -739,10 +806,9 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ break; } - index++; - index %= audioQueueBufferRefLookupCount; + index = (index + 1) % audioQueueBufferRefLookupCount; } - + audioPacketsPlayedCount++; if (bufferIndex == -1) @@ -757,10 +823,12 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ } pthread_mutex_lock(&queueBuffersMutex); - + bufferUsed[bufferIndex] = false; numberOfBuffersUsed--; + BOOL signal = NO; + if (!audioQueueFlushing) { QueueEntry* entry = currentlyPlayingEntry; @@ -779,13 +847,33 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ }); CFRunLoopWakeUp([playbackThreadRunLoop getCFRunLoop]); + + signal = YES; } } } } - // No need to signal constantly if we're reseting the AudioQueue - if ((audioQueueFlushing && numberOfBuffersUsed < 5) || !audioQueueFlushing) + if (audioQueueFlushing) + { + signal = signal || (audioQueueFlushing && numberOfBuffersUsed < 8); + } + else + { + if (seekToTimeWasRequested) + { + signal = YES; + } + else + { + if (waiting && numberOfBuffersUsed < audioQueueBufferCount / 2) + { + signal = YES; + } + } + } + + if (signal) { pthread_cond_signal(&queueBufferReadyCondition); } @@ -801,9 +889,9 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ } if (propertyId == kAudioQueueProperty_IsRunning) - { + { if (![self audioQueueIsRunning] && self.internalState == AudioPlayerInternalStateStopping) - { + { self.internalState = AudioPlayerInternalStateStopped; } else if (self.internalState == AudioPlayerInternalStateWaitingForQueueToStart) @@ -817,20 +905,24 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ -(void) enqueueBuffer { - @synchronized(self) + pthread_mutex_lock(&playerMutex); { OSStatus error; - + if (audioFileStream == 0) { + pthread_mutex_unlock(&playerMutex); + return; } if (self.internalState == AudioPlayerInternalStateStopped) { + pthread_mutex_unlock(&playerMutex); + return; } - + pthread_mutex_lock(&queueBuffersMutex); bufferUsed[fillBufferIndex] = true; @@ -856,6 +948,8 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ if (error) { + pthread_mutex_unlock(&playerMutex); + return; } @@ -863,6 +957,8 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ { if (![self startAudioQueue]) { + pthread_mutex_unlock(&playerMutex); + return; } } @@ -875,20 +971,32 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ bytesFilled = 0; packetsFilled = 0; } - - pthread_mutex_lock(&queueBuffersMutex); - + pthread_mutex_unlock(&playerMutex); + + pthread_mutex_lock(&queueBuffersMutex); + while (bufferUsed[fillBufferIndex]) { + waiting = YES; + + if (numberOfBuffersUsed == 0) + { + memset(&bufferUsed[0], 0, sizeof(bool) * audioQueueBufferCount); + + break; + } + pthread_cond_wait(&queueBufferReadyCondition, &queueBuffersMutex); } - + + waiting = NO; + pthread_mutex_unlock(&queueBuffersMutex); } -(void) didEncounterError:(AudioPlayerErrorCode)errorCodeIn { - errorCode = errorCode; + errorCode = errorCode; self.internalState = AudioPlayerInternalStateError; } @@ -907,7 +1015,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ } currentAudioStreamBasicDescription = currentlyPlayingEntry->audioStreamBasicDescription; - + error = AudioQueueNewOutput(¤tlyPlayingEntry->audioStreamBasicDescription, AudioQueueOutputCallbackProc, (__bridge void*)self, NULL, NULL, 0, &audioQueue); if (error) @@ -927,11 +1035,13 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ error = AudioQueueSetProperty(audioQueue, kAudioQueueProperty_HardwareCodecPolicy, &val, sizeof(UInt32)); + AudioQueueSetParameter(audioQueue, kAudioQueueParam_Volume, 1); + if (error) { } #endif - + memset(audioQueueBufferLookup, 0, sizeof(AudioQueueBufferRefLookupEntry) * audioQueueBufferRefLookupCount); // Allocate AudioQueue buffers @@ -968,7 +1078,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ audioPacketsReadCount = 0; audioPacketsPlayedCount = 0; - + // Get file cookie/magic bytes information UInt32 cookieSize; @@ -1045,9 +1155,9 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ if (runLoop) { CFRunLoopPerformBlock([runLoop getCFRunLoop], NSDefaultRunLoopMode, ^ - { - [self processRunloop]; - }); + { + [self processRunloop]; + }); CFRunLoopWakeUp([runLoop getCFRunLoop]); } @@ -1055,22 +1165,23 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ -(void) seekToTime:(double)value { - @synchronized(self) + pthread_mutex_lock(&playerMutex); { BOOL seekAlreadyRequested = seekToTimeWasRequested; seekToTimeWasRequested = YES; requestedSeekTime = value; - + if (!seekAlreadyRequested) { [self wakeupPlaybackThread]; } } + pthread_mutex_unlock(&playerMutex); } -(void) processQueue:(BOOL)skipCurrent -{ +{ if (playbackThread == nil) { newFileToPlay = YES; @@ -1094,10 +1205,10 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ } } --(void) setCurrentlyReadingEntry:(QueueEntry*)entry andStartPlaying:(BOOL)startPlaying +-(void) setCurrentlyReadingEntry:(QueueEntry*)entry andStartPlaying:(BOOL)startPlaying { OSStatus error; - + pthread_mutex_lock(&queueBuffersMutex); if (startPlaying) @@ -1105,13 +1216,13 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ if (audioQueue) { pthread_mutex_unlock(&queueBuffersMutex); - + [self resetAudioQueue]; pthread_mutex_lock(&queueBuffersMutex); } } - + if (audioFileStream) { AudioFileStreamClose(audioFileStream); @@ -1141,7 +1252,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ { [bufferingQueue removeAllObjects]; - [self processDidFinishPlaying:currentlyPlayingEntry withNext:entry]; + [self processDidFinishPlaying:currentlyPlayingEntry withNext:entry]; } else { @@ -1153,6 +1264,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ -(void) audioQueueFinishedPlaying:(QueueEntry*)entry { + pthread_mutex_lock(&playerMutex); pthread_mutex_lock(&queueBuffersMutex); QueueEntry* next = [bufferingQueue dequeue]; @@ -1160,6 +1272,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ [self processDidFinishPlaying:entry withNext:next]; pthread_mutex_unlock(&queueBuffersMutex); + pthread_mutex_unlock(&playerMutex); } -(void) processDidFinishPlaying:(QueueEntry*)entry withNext:(QueueEntry*)next @@ -1168,7 +1281,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ { return; } - + NSObject* queueItemId = entry.queueItemId; double progress = [entry progress]; double duration = [entry duration]; @@ -1180,7 +1293,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ if (nextIsDifferent) { next->seekTime = 0; - + seekToTimeWasRequested = NO; } @@ -1192,17 +1305,17 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ if (nextIsDifferent && entry) { dispatch_async(dispatch_get_main_queue(), ^ - { - [self.delegate audioPlayer:self didFinishPlayingQueueItemId:queueItemId withReason:stopReason andProgress:progress andDuration:duration]; - }); + { + [self.delegate audioPlayer:self didFinishPlayingQueueItemId:queueItemId withReason:stopReason andProgress:progress andDuration:duration]; + }); } if (nextIsDifferent) { dispatch_async(dispatch_get_main_queue(), ^ - { - [self.delegate audioPlayer:self didStartPlayingQueueItemId:playingQueueItemId]; - }); + { + [self.delegate audioPlayer:self didStartPlayingQueueItemId:playingQueueItemId]; + }); } } else @@ -1217,19 +1330,21 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ if (nextIsDifferent && entry) { dispatch_async(dispatch_get_main_queue(), ^ - { - [self.delegate audioPlayer:self didFinishPlayingQueueItemId:queueItemId withReason:stopReason andProgress:progress andDuration:duration]; - }); + { + [self.delegate audioPlayer:self didFinishPlayingQueueItemId:queueItemId withReason:stopReason andProgress:progress andDuration:duration]; + }); } } } -(BOOL) processRunloop { - @synchronized(self) + pthread_mutex_lock(&playerMutex); { if (self.internalState == AudioPlayerInternalStatePaused) { + pthread_mutex_unlock(&playerMutex); + return YES; } else if (newFileToPlay) @@ -1240,9 +1355,9 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ [self setCurrentlyReadingEntry:entry andStartPlaying:YES]; - newFileToPlay = NO; + newFileToPlay = NO; nextIsIncompatible = NO; - } + } else if (seekToTimeWasRequested && currentlyPlayingEntry && currentlyPlayingEntry != currentlyReadingEntry) { currentlyPlayingEntry.bufferIndex = -1; @@ -1253,31 +1368,6 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ nextIsIncompatible = NO; } - else if (currentlyReadingEntry == nil) - { - if (nextIsIncompatible && currentlyPlayingEntry != nil) - { - // Holding off cause next is incompatible - } - else - { - if (upcomingQueue.count > 0) - { - QueueEntry* entry = [upcomingQueue dequeue]; - - BOOL startPlaying = currentlyPlayingEntry == nil; - - [self setCurrentlyReadingEntry:entry andStartPlaying:startPlaying]; - } - else if (currentlyPlayingEntry == nil) - { - if (self.internalState != AudioPlayerInternalStateStopped) - { - [self stopAudioQueue]; - } - } - } - } else if (self.internalState == AudioPlayerInternalStateStopped && stopReason == AudioPlayerStopReasonUserAction) { [self stopAudioQueue]; @@ -1314,7 +1404,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ } pthread_mutex_lock(&queueBuffersMutex); - + if ([bufferingQueue peek] == currentlyPlayingEntry) { [bufferingQueue dequeue]; @@ -1326,12 +1416,40 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ [self resetAudioQueue]; } + else if (currentlyReadingEntry == nil) + { + if (nextIsIncompatible && currentlyPlayingEntry != nil) + { + // Holding off cause next is incompatible + } + else + { + if (upcomingQueue.count > 0) + { + QueueEntry* entry = [upcomingQueue dequeue]; + + BOOL startPlaying = currentlyPlayingEntry == nil; + + [self setCurrentlyReadingEntry:entry andStartPlaying:startPlaying]; + } + else if (currentlyPlayingEntry == nil) + { + if (self.internalState != AudioPlayerInternalStateStopped) + { + [self stopAudioQueue]; + } + } + } + } if (disposeWasRequested) { + pthread_mutex_unlock(&playerMutex); + return NO; } } + pthread_mutex_unlock(&playerMutex); if (currentlyReadingEntry && currentlyReadingEntry->parsedHeader && currentlyReadingEntry != currentlyPlayingEntry) { @@ -1372,7 +1490,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ [playbackThreadRunLoop addPort:[NSPort port] forMode:NSDefaultRunLoopMode]; do - { + { [playbackThreadRunLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:5]]; if (![self processRunloop]) @@ -1384,16 +1502,16 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ disposeWasRequested = NO; seekToTimeWasRequested = NO; - + currentlyReadingEntry.dataSource.delegate = nil; currentlyPlayingEntry.dataSource.delegate = nil; currentlyReadingEntry = nil; currentlyPlayingEntry = nil; - self.internalState = AudioPlayerInternalStateDisposed; + self.internalState = AudioPlayerInternalStateDisposed; - [threadFinishedCondLock lock]; + [threadFinishedCondLock lock]; [threadFinishedCondLock unlockWithCondition:1]; } @@ -1436,14 +1554,15 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ seekByteOffset = packetAlignedByteOffset + currentlyPlayingEntry->audioDataOffset; } } - + + [currentlyReadingEntry updateAudioDataSource]; [currentlyReadingEntry.dataSource seekToOffset:seekByteOffset]; if (seekByteOffset > 0) { discontinuous = YES; - } - + } + if (audioQueue) { [self resetAudioQueue]; @@ -1455,9 +1574,11 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ -(BOOL) startAudioQueue { OSStatus error; - + self.internalState = AudioPlayerInternalStateWaitingForQueueToStart; + AudioQueueSetParameter(audioQueue, kAudioQueueParam_Volume, 1); + error = AudioQueueStart(audioQueue, NULL); if (error) @@ -1479,7 +1600,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ } -(void) stopAudioQueue -{ +{ OSStatus error; if (!audioQueue) @@ -1489,7 +1610,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ return; } else - { + { audioQueueFlushing = YES; error = AudioQueueStop(audioQueue, true); @@ -1508,10 +1629,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ { numberOfBuffersUsed = 0; - for (int i = 0; i < audioQueueBufferCount; i++) - { - bufferUsed[i] = false; - } + memset(&bufferUsed[0], 0, sizeof(bool) * audioQueueBufferCount); } pthread_cond_signal(&queueBufferReadyCondition); @@ -1544,9 +1662,9 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ if (error) { dispatch_async(dispatch_get_main_queue(), ^ - { - [self didEncounterError:AudioPlayerErrorQueueStopFailed];; - }); + { + [self didEncounterError:AudioPlayerErrorQueueStopFailed];; + }); } pthread_mutex_lock(&queueBuffersMutex); @@ -1558,7 +1676,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ for (int i = 0; i < audioQueueBufferCount; i++) { bufferUsed[i] = false; - } + } } pthread_cond_signal(&queueBufferReadyCondition); @@ -1572,7 +1690,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ { currentlyPlayingEntry->lastProgress = 0; } - + audioPacketsReadCount = 0; audioPacketsPlayedCount = 0; audioQueueFlushing = NO; @@ -1581,7 +1699,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ -(void) dataSourceDataAvailable:(DataSource*)dataSourceIn { OSStatus error; - + if (currentlyReadingEntry.dataSource != dataSourceIn) { return; @@ -1656,11 +1774,11 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ NSObject* queueItemId = currentlyReadingEntry.queueItemId; dispatch_async(dispatch_get_main_queue(), ^ - { - [self.delegate audioPlayer:self didFinishBufferingSourceWithQueueItemId:queueItemId]; - }); - - @synchronized(self) + { + [self.delegate audioPlayer:self didFinishBufferingSourceWithQueueItemId:queueItemId]; + }); + + pthread_mutex_lock(&playerMutex); { if (audioQueue) { @@ -1673,11 +1791,12 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ self.internalState = AudioPlayerInternalStateStopped; } } + pthread_mutex_unlock(&playerMutex); } -(void) pause { - @synchronized(self) + pthread_mutex_lock(&playerMutex); { OSStatus error; @@ -1693,6 +1812,8 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ { [self didEncounterError:AudioPlayerErrorQueuePauseFailed]; + pthread_mutex_unlock(&playerMutex); + return; } } @@ -1700,11 +1821,12 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ [self wakeupPlaybackThread]; } } + pthread_mutex_unlock(&playerMutex); } -(void) resume { - @synchronized(self) + pthread_mutex_lock(&playerMutex); { OSStatus error; @@ -1723,20 +1845,25 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ { [self didEncounterError:AudioPlayerErrorQueueStartFailed]; + pthread_mutex_unlock(&playerMutex); + return; } [self wakeupPlaybackThread]; } } + pthread_mutex_unlock(&playerMutex); } -(void) stop { - @synchronized(self) + pthread_mutex_lock(&playerMutex); { if (self.internalState == AudioPlayerInternalStateStopped) { + pthread_mutex_unlock(&playerMutex); + return; } @@ -1745,14 +1872,17 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ [self wakeupPlaybackThread]; } + pthread_mutex_unlock(&playerMutex); } -(void) flushStop { - @synchronized(self) + pthread_mutex_lock(&playerMutex); { if (self.internalState == AudioPlayerInternalStateStopped) { + pthread_mutex_unlock(&playerMutex); + return; } @@ -1761,13 +1891,14 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ [self wakeupPlaybackThread]; } + pthread_mutex_unlock(&playerMutex); } -(void) stopThread { BOOL wait = NO; - @synchronized(self) + pthread_mutex_lock(&playerMutex); { disposeWasRequested = YES; @@ -1778,6 +1909,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ CFRunLoopStop([playbackThreadRunLoop getCFRunLoop]); } } + pthread_mutex_unlock(&playerMutex); if (wait) { diff --git a/Audjustable/Classes/AudioPlayer/AutoRecoveringHttpDataSource.m b/Audjustable/Classes/AudioPlayer/AutoRecoveringHttpDataSource.m index 755143a..6c2b450 100644 --- a/Audjustable/Classes/AudioPlayer/AutoRecoveringHttpDataSource.m +++ b/Audjustable/Classes/AudioPlayer/AutoRecoveringHttpDataSource.m @@ -97,6 +97,7 @@ static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReach { if (reachabilityRef != NULL) { + SCNetworkReachabilitySetCallback(reachabilityRef, NULL, NULL); SCNetworkReachabilityUnscheduleFromRunLoop(reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); } } @@ -115,6 +116,8 @@ static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReach -(void) dealloc { + self.innerDataSource.delegate = nil; + [self stopNotifier]; if (reachabilityRef!= NULL) diff --git a/Audjustable/Classes/AudioPlayer/DataSource.h b/Audjustable/Classes/AudioPlayer/DataSource.h index ad9f2d0..1ee0ab7 100644 --- a/Audjustable/Classes/AudioPlayer/DataSource.h +++ b/Audjustable/Classes/AudioPlayer/DataSource.h @@ -42,6 +42,11 @@ -(void) dataSourceEof:(DataSource*)dataSource; @end +@protocol AudioDataSource +@property (readwrite) double averageBitRate; +@property (readwrite) long long audioDataOffset; +@end + @interface DataSource : NSObject @property (readonly) long long position;