diff --git a/StreamingKit/StreamingKit/STKAudioPlayer b/StreamingKit/StreamingKit/STKAudioPlayer deleted file mode 100644 index 40498c5..0000000 --- a/StreamingKit/StreamingKit/STKAudioPlayer +++ /dev/null @@ -1,3058 +0,0 @@ -/********************************************************************************** - AudioPlayer.m - - Created by Thong Nguyen on 14/05/2012. - https://github.com/tumtumtum/audjustable - - Inspired by Matt Gallagher's AudioStreamer: - https://github.com/mattgallagher/AudioStreamer - - Copyright (c) 2012 Thong Nguyen (tumtumtum@gmail.com). All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - 3. All advertising materials mentioning features or use of this software - must display the following acknowledgement: - This product includes software developed by Thong Nguyen (tumtumtum@gmail.com) - 4. Neither the name of Thong Nguyen nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY Thong Nguyen''AS IS'' AND ANY - EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - 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 "STKAudioPlayer.h" -#import "AudioToolbox/AudioToolbox.h" -#import "STKHTTPDataSource.h" -#import "STKLocalFileDataSource.h" -#import "libkern/OSAtomic.h" - -#define STK_BIT_RATE_ESTIMATION_MIN_PACKETS (64) -#define STK_BUFFERS_NEEDED_TO_START (32) -#define STK_BUFFERS_NEEDED_WHEN_UNDERUNNING (128) -#define STK_DEFAULT_READ_BUFFER_SIZE (2 * 1024) -#define STK_DEFAULT_PACKET_BUFFER_SIZE (2048) -#define STK_FRAMES_MISSED_BEFORE_CONSIDERED_UNDERRUN (1024) -#define STK_DEFAULT_NUMBER_OF_AUDIOQUEUE_BUFFERS (1024) - -#define OSSTATUS_PARAM_ERROR (-50) - -#define LOGINFO(x) [self logInfo:[NSString stringWithFormat:@"%s %@", sel_getName(_cmd), x]]; - -typedef struct -{ - AudioQueueBufferRef ref; - int bufferIndex; -} -AudioQueueBufferRefLookupEntry; - -@interface NSMutableArray(AudioPlayerExtensions) --(void) enqueue:(id)obj; --(id) dequeue; --(id) peek; -@end - -@implementation NSMutableArray(AudioPlayerExtensions) - --(void) enqueue:(id)obj -{ - [self insertObject:obj atIndex:0]; -} - --(void) skipQueue:(id)obj -{ - [self addObject:obj]; -} - --(id) dequeue -{ - if ([self count] == 0) - { - return nil; - } - - id retval = [self lastObject]; - - [self removeLastObject]; - - return retval; -} - --(id) peek -{ - return [self lastObject]; -} - --(id) peekRecent -{ - if (self.count == 0) - { - return nil; - } - - return [self objectAtIndex:0]; -} - -@end - -@interface STKQueueEntry : NSObject -{ -@public - BOOL parsedHeader; - double sampleRate; - double lastProgress; - double packetDuration; - UInt64 audioDataOffset; - UInt64 audioDataByteCount; - UInt32 packetBufferSize; - volatile BOOL cancel; - volatile int processedPacketsCount; - volatile int processedPacketsSizeTotal; - AudioStreamBasicDescription audioStreamBasicDescription; -} -@property (readwrite, retain) NSObject* queueItemId; -@property (readwrite, retain) STKDataSource* dataSource; -@property (readwrite) Float64 seekTime; -@property (readwrite) int bytesBuffered; -@property (readwrite) int lastByteIndex; -@property (readwrite) Float64 lastFrameIndex; -@property (readwrite) Float64 firstFrameIndex; -@property (readonly) UInt64 audioDataLengthInBytes; - --(double) duration; --(double) calculatedBitRate; - --(id) initWithDataSource:(STKDataSource*)dataSource andQueueItemId:(NSObject*)queueItemId; - -@end - -@implementation STKQueueEntry - --(id) initWithDataSource:(STKDataSource*)dataSourceIn andQueueItemId:(NSObject*)queueItemIdIn -{ - if (self = [super init]) - { - self.dataSource = dataSourceIn; - self.queueItemId = queueItemIdIn; - self.lastFrameIndex = -1; - self.lastByteIndex = -1; - } - - return self; -} - --(double) calculatedBitRate -{ - double retval; - - if (packetDuration && processedPacketsCount > STK_BIT_RATE_ESTIMATION_MIN_PACKETS) - { - double averagePacketByteSize = processedPacketsSizeTotal / processedPacketsCount; - - retval = averagePacketByteSize / packetDuration * 8; - - return retval; - } - - retval = (audioStreamBasicDescription.mBytesPerFrame * audioStreamBasicDescription.mSampleRate) * 8; - - 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; - } -} - --(Float64) calculateProgressWithTotalFramesPlayed:(Float64)framesPlayed -{ - return (Float64)self.seekTime + ((framesPlayed - self.firstFrameIndex) / (Float64)self->audioStreamBasicDescription.mSampleRate); -} - --(double) calculateProgressWithBytesPlayed:(Float64)bytesPlayed -{ - double retval = lastProgress; - double duration = [self duration]; - - if (self->sampleRate > 0) - { - double calculatedBitrate = [self calculatedBitRate]; - - retval = bytesPlayed / calculatedBitrate * 8; - - retval = self.seekTime + retval; - - [self updateAudioDataSource]; - } - - if (retval > duration) - { - retval = duration; - } - - return retval; -} - --(double) duration -{ - if (self->sampleRate <= 0) - { - return 0; - } - - UInt64 audioDataLengthInBytes = [self audioDataLengthInBytes]; - - double calculatedBitRate = [self calculatedBitRate]; - - if (calculatedBitRate < 1.0 || self.dataSource.length == 0) - { - return 0; - } - - return audioDataLengthInBytes / (calculatedBitRate / 8); -} - --(UInt64) audioDataLengthInBytes -{ - if (audioDataByteCount) - { - return audioDataByteCount; - } - else - { - if (!self.dataSource.length) - { - return 0; - } - - return self.dataSource.length - audioDataOffset; - } -} - --(BOOL) isKnownToBeIncompatible:(AudioStreamBasicDescription*)basicDescription -{ - if (self->audioStreamBasicDescription.mSampleRate == 0) - { - return NO; - } - - return (memcmp(&(self->audioStreamBasicDescription), basicDescription, sizeof(*basicDescription)) != 0); -} - --(BOOL) couldBeIncompatible:(AudioStreamBasicDescription*)basicDescription -{ - if (self->audioStreamBasicDescription.mSampleRate == 0) - { - return YES; - } - - return memcmp(&(self->audioStreamBasicDescription), basicDescription, sizeof(*basicDescription)) != 0; -} - --(NSString*) description -{ - return [[self queueItemId] description]; -} - -@end - -@interface STKAudioPlayer() -{ - UInt8* readBuffer; - int readBufferSize; - - NSOperationQueue* asyncApiRequestQueue; - - STKQueueEntry* currentlyPlayingEntry; - STKQueueEntry* currentlyReadingEntry; - - NSMutableArray* upcomingQueue; - NSMutableArray* bufferingQueue; - - AudioQueueBufferRef* audioQueueBuffer; - AudioQueueBufferRefLookupEntry* audioQueueBufferLookup; - unsigned int audioQueueBufferRefLookupCount; - unsigned int audioQueueBufferCount; - AudioStreamPacketDescription* packetDescs; - bool* bufferUsed; - int numberOfBuffersUsed; - - AudioQueueRef audioQueue; - AudioStreamBasicDescription currentAudioStreamBasicDescription; - - NSThread* playbackThread; - NSRunLoop* playbackThreadRunLoop; - NSConditionLock* threadFinishedCondLock; - - AudioFileStreamID audioFileStream; - - BOOL discontinuous; - - int bytesFilled; - int packetsFilled; - int framesFilled; - int fillBufferIndex; - - volatile Float64 framesQueued; - volatile Float64 timelineAdjust; - volatile Float64 rebufferingStartFrames; - -#if TARGET_OS_IPHONE - UIBackgroundTaskIdentifier backgroundTaskId; -#endif - - AudioPlayerErrorCode errorCode; - AudioPlayerStopReason stopReason; - - int seekLock; - int32_t seekVersion; - int currentEntryReferencesLock; - pthread_mutex_t playerMutex; - pthread_mutex_t queueBuffersMutex; - pthread_cond_t queueBufferReadyCondition; - - pthread_mutex_t mainThreadSyncCallMutex; - pthread_cond_t mainThreadSyncCallReadyCondition; - - volatile BOOL waiting; - volatile BOOL disposeWasRequested; - volatile BOOL seekToTimeWasRequested; - volatile BOOL newFileToPlay; - volatile double requestedSeekTime; - volatile BOOL audioQueueFlushing; - volatile SInt64 audioPacketsReadCount; - volatile SInt64 audioPacketsPlayedCount; - - BOOL meteringEnabled; - AudioQueueLevelMeterState* levelMeterState; - NSInteger numberOfChannels; -} - -@property (readwrite) AudioPlayerInternalState internalState; -@property (readwrite) AudioPlayerInternalState stateBeforePaused; - --(void) logInfo:(NSString*)line; --(void) createAudioQueue; --(void) enqueueBuffer; --(void) resetAudioQueueWithReason:(NSString*)reason; --(BOOL) startAudioQueue; --(void) stopAudioQueueWithReason:(NSString*)reason; --(BOOL) processRunloop; --(void) wakeupPlaybackThread; --(void) audioQueueFinishedPlaying:(STKQueueEntry*)entry; --(void) processSeekToTime; --(void) didEncounterError:(AudioPlayerErrorCode)errorCode; --(void) setInternalState:(AudioPlayerInternalState)value; --(void) processFinishPlayingIfAnyAndPlayingNext:(STKQueueEntry*)entry withNext:(STKQueueEntry*)next; --(void) handlePropertyChangeForFileStream:(AudioFileStreamID)audioFileStreamIn fileStreamPropertyID:(AudioFileStreamPropertyID)propertyID ioFlags:(UInt32*)ioFlags; --(void) handleAudioPackets:(const void*)inputData numberBytes:(UInt32)numberBytes numberPackets:(UInt32)numberPackets packetDescriptions:(AudioStreamPacketDescription*)packetDescriptions; --(void) handleAudioQueueOutput:(AudioQueueRef)audioQueue buffer:(AudioQueueBufferRef)buffer; --(void) handlePropertyChangeForQueue:(AudioQueueRef)audioQueue propertyID:(AudioQueuePropertyID)propertyID; -@end - -static void AudioFileStreamPropertyListenerProc(void* clientData, AudioFileStreamID audioFileStream, AudioFileStreamPropertyID propertyId, UInt32* flags) -{ - STKAudioPlayer* player = (__bridge STKAudioPlayer*)clientData; - - [player handlePropertyChangeForFileStream:audioFileStream fileStreamPropertyID:propertyId ioFlags:flags]; -} - -static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UInt32 numberPackets, const void* inputData, AudioStreamPacketDescription* packetDescriptions) -{ - STKAudioPlayer* player = (__bridge STKAudioPlayer*)clientData; - - [player handleAudioPackets:inputData numberBytes:numberBytes numberPackets:numberPackets packetDescriptions:packetDescriptions]; -} - -static void AudioQueueOutputCallbackProc(void* clientData, AudioQueueRef audioQueue, AudioQueueBufferRef buffer) -{ - STKAudioPlayer* player = (__bridge STKAudioPlayer*)clientData; - - [player handleAudioQueueOutput:audioQueue buffer:buffer]; -} - -static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQueue, AudioQueuePropertyID propertyId) -{ - STKAudioPlayer* player = (__bridge STKAudioPlayer*)userData; - - [player handlePropertyChangeForQueue:audioQueue propertyID:propertyId]; -} - -@implementation STKAudioPlayer -@synthesize delegate, internalState, state; - --(AudioPlayerInternalState) internalState -{ - return internalState; -} - --(void) setInternalState:(AudioPlayerInternalState)value -{ - if (value == internalState) - { - return; - } - - internalState = value; - - if ([self.delegate respondsToSelector:@selector(audioPlayer:internalStateChanged:)]) - { - dispatch_async(dispatch_get_main_queue(), ^ - { - [self.delegate audioPlayer:self internalStateChanged:internalState]; - }); - } - - AudioPlayerState newState; - - switch (internalState) - { - case AudioPlayerInternalStateInitialised: - newState = AudioPlayerStateReady; - break; - case AudioPlayerInternalStateRunning: - case AudioPlayerInternalStateStartingThread: - case AudioPlayerInternalStatePlaying: - case AudioPlayerInternalStateWaitingForDataAfterSeek: - case AudioPlayerInternalStateFlushingAndStoppingButStillPlaying: - newState = AudioPlayerStatePlaying; - break; - case AudioPlayerInternalStateRebuffering: - case AudioPlayerInternalStateWaitingForData: - newState = AudioPlayerStateBuffering; - break; - case AudioPlayerInternalStateStopping: - case AudioPlayerInternalStateStopped: - newState = AudioPlayerStateStopped; - break; - case AudioPlayerInternalStatePaused: - newState = AudioPlayerStatePaused; - break; - case AudioPlayerInternalStateDisposed: - newState = AudioPlayerStateDisposed; - break; - case AudioPlayerInternalStateError: - newState = AudioPlayerStateError; - break; - } - - if (newState != self.state) - { - self.state = newState; - - dispatch_async(dispatch_get_main_queue(), ^ - { - [self.delegate audioPlayer:self stateChanged:self.state]; - }); - } -} - --(AudioPlayerStopReason) stopReason -{ - return stopReason; -} - --(BOOL) audioQueueIsRunning -{ - UInt32 isRunning; - UInt32 isRunningSize = sizeof(isRunning); - - AudioQueueGetProperty(audioQueue, kAudioQueueProperty_IsRunning, &isRunning, &isRunningSize); - - return isRunning ? YES : NO; -} - --(void) logInfo:(NSString*)line -{ - if ([NSThread currentThread].isMainThread) - { - if ([self->delegate respondsToSelector:@selector(audioPlayer:logInfo:)]) - { - [self->delegate audioPlayer:self logInfo:line]; - } - } - else - { - if ([self->delegate respondsToSelector:@selector(audioPlayer:logInfo:)]) - { - [self->delegate audioPlayer:self logInfo:line]; - } - } -} - --(id) init -{ - return [self initWithNumberOfAudioQueueBuffers:STK_DEFAULT_NUMBER_OF_AUDIOQUEUE_BUFFERS andReadBufferSize:STK_DEFAULT_READ_BUFFER_SIZE]; -} - --(id) initWithNumberOfAudioQueueBuffers:(int)numberOfAudioQueueBuffers andReadBufferSize:(int)readBufferSizeIn -{ - if (self = [super init]) - { - asyncApiRequestQueue = [[NSOperationQueue alloc] init]; - [asyncApiRequestQueue setMaxConcurrentOperationCount:1]; - - readBufferSize = readBufferSizeIn; - readBuffer = calloc(sizeof(UInt8), readBufferSize); - - audioQueueBufferCount = numberOfAudioQueueBuffers; - audioQueueBuffer = calloc(sizeof(AudioQueueBufferRef), audioQueueBufferCount); - - audioQueueBufferRefLookupCount = audioQueueBufferCount * 2; - audioQueueBufferLookup = calloc(sizeof(AudioQueueBufferRefLookupEntry), audioQueueBufferRefLookupCount); - - packetDescs = calloc(sizeof(AudioStreamPacketDescription), audioQueueBufferCount); - bufferUsed = calloc(sizeof(bool), audioQueueBufferCount); - - pthread_mutexattr_t attr; - - pthread_mutexattr_init(&attr); - pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); - - pthread_mutex_init(&playerMutex, &attr); - pthread_mutex_init(&queueBuffersMutex, NULL); - pthread_cond_init(&queueBufferReadyCondition, NULL); - - pthread_mutex_init(&mainThreadSyncCallMutex, NULL); - pthread_cond_init(&mainThreadSyncCallReadyCondition, NULL); - - threadFinishedCondLock = [[NSConditionLock alloc] initWithCondition:0]; - - self.internalState = AudioPlayerInternalStateInitialised; - - upcomingQueue = [[NSMutableArray alloc] init]; - bufferingQueue = [[NSMutableArray alloc] init]; - } - - return self; -} - --(void) dealloc -{ - if (currentlyReadingEntry) - { - currentlyReadingEntry.dataSource.delegate = nil; - } - - if (currentlyPlayingEntry) - { - currentlyPlayingEntry.dataSource.delegate = nil; - } - - pthread_mutex_destroy(&playerMutex); - pthread_mutex_destroy(&queueBuffersMutex); - pthread_cond_destroy(&queueBufferReadyCondition); - - pthread_mutex_destroy(&mainThreadSyncCallMutex); - pthread_cond_destroy(&mainThreadSyncCallReadyCondition); - - - if (audioFileStream) - { - AudioFileStreamClose(audioFileStream); - } - - if (audioQueue) - { - AudioQueueDispose(audioQueue, true); - } - - free(bufferUsed); - free(readBuffer); - free(packetDescs); - free(audioQueueBuffer); - free(audioQueueBufferLookup); - free(levelMeterState); -} - --(void) startSystemBackgroundTask -{ -#if TARGET_OS_IPHONE - pthread_mutex_lock(&playerMutex); - { - if (backgroundTaskId != UIBackgroundTaskInvalid) - { - pthread_mutex_unlock(&playerMutex); - - return; - } - - backgroundTaskId = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^ - { - [self stopSystemBackgroundTask]; - }]; - } - pthread_mutex_unlock(&playerMutex); -#endif -} - --(void) stopSystemBackgroundTask -{ -#if TARGET_OS_IPHONE - pthread_mutex_lock(&playerMutex); - { - if (backgroundTaskId != UIBackgroundTaskInvalid) - { - [[UIApplication sharedApplication] endBackgroundTask:backgroundTaskId]; - - backgroundTaskId = UIBackgroundTaskInvalid; - } - } - pthread_mutex_unlock(&playerMutex); -#endif -} - --(STKDataSource*) dataSourceFromURL:(NSURL*)url -{ - STKDataSource* retval; - - if ([url.scheme isEqualToString:@"file"]) - { - retval = [[STKLocalFileDataSource alloc] initWithFilePath:url.path]; - } - else - { - retval = [[STKHTTPDataSource alloc] initWithURL:url]; - } - - return retval; -} - --(void) clearQueue -{ - [self clearQueueIncludingUpcoming:YES]; -} - --(void) clearQueueIncludingUpcoming:(BOOL)includeUpcoming -{ - pthread_mutex_lock(&playerMutex); - { - NSMutableArray* array = [[NSMutableArray alloc] initWithCapacity:bufferingQueue.count + (includeUpcoming ? upcomingQueue.count : 0)]; - - STKQueueEntry* entry = [bufferingQueue dequeue]; - - if (entry && entry != currentlyPlayingEntry) - { - [array addObject:[entry queueItemId]]; - } - - while (bufferingQueue.count > 0) - { - id queueItemId = [[bufferingQueue dequeue] queueItemId]; - - if (queueItemId != nil) - { - [array addObject:queueItemId]; - } - } - - if (includeUpcoming) - { - for (STKQueueEntry* entry in upcomingQueue) - { - [array addObject:entry.queueItemId]; - } - - [upcomingQueue removeAllObjects]; - } - - if (array.count > 0) - { - [self playbackThreadQueueMainThreadSyncBlock:^ - { - if ([self.delegate respondsToSelector:@selector(audioPlayer:didCancelQueuedItems:)]) - { - [self.delegate audioPlayer:self didCancelQueuedItems:array]; - } - }]; - } - } - pthread_mutex_unlock(&playerMutex); -} - --(void) play:(NSString*)urlString -{ - NSURL* url = [NSURL URLWithString:urlString]; - - [self setDataSource:[self dataSourceFromURL:url] withQueueItemId:urlString]; -} - --(void) playWithURL:(NSURL*)url -{ - [self setDataSource:[self dataSourceFromURL:url] withQueueItemId:url]; -} - --(void) playWithDataSource:(STKDataSource*)dataSource -{ - [self setDataSource:dataSource withQueueItemId:dataSource]; -} - --(void) setDataSource:(STKDataSource*)dataSourceIn withQueueItemId:(NSObject*)queueItemId -{ - [asyncApiRequestQueue cancelAllOperations]; - - [asyncApiRequestQueue addOperationWithBlock:^ - { - pthread_mutex_lock(&playerMutex); - { - LOGINFO(([NSString stringWithFormat:@"Playing: %@", [queueItemId description]])); - - [self startSystemBackgroundTask]; - - [self resetAudioQueueWithReason:@"from skipCurrent"]; - [self clearQueue]; - - [upcomingQueue enqueue:[[STKQueueEntry alloc] initWithDataSource:dataSourceIn andQueueItemId:queueItemId]]; - - self.internalState = AudioPlayerInternalStateRunning; - - newFileToPlay = YES; - - [self createOrWakeupPlaybackThread]; - } - pthread_mutex_unlock(&playerMutex); - }]; -} - --(void) queueDataSource:(STKDataSource*)dataSourceIn withQueueItemId:(NSObject*)queueItemId -{ - [asyncApiRequestQueue addOperationWithBlock:^ - { - pthread_mutex_lock(&playerMutex); - { - [upcomingQueue enqueue:[[STKQueueEntry alloc] initWithDataSource:dataSourceIn andQueueItemId:queueItemId]]; - - [self createOrWakeupPlaybackThread]; - } - pthread_mutex_unlock(&playerMutex); - }]; -} - --(void) handlePropertyChangeForFileStream:(AudioFileStreamID)inAudioFileStream fileStreamPropertyID:(AudioFileStreamPropertyID)inPropertyID ioFlags:(UInt32*)ioFlags -{ - OSStatus error; - - if (!currentlyReadingEntry) - { - return; - } - - switch (inPropertyID) - { - case kAudioFileStreamProperty_DataOffset: - { - SInt64 offset; - UInt32 offsetSize = sizeof(offset); - - AudioFileStreamGetProperty(audioFileStream, kAudioFileStreamProperty_DataOffset, &offsetSize, &offset); - - currentlyReadingEntry->parsedHeader = YES; - currentlyReadingEntry->audioDataOffset = offset; - - [currentlyReadingEntry updateAudioDataSource]; - - break; - } - case kAudioFileStreamProperty_FileFormat: - { - char fileFormat[4]; - UInt32 fileFormatSize = sizeof(fileFormat); - - AudioFileStreamGetProperty(inAudioFileStream, kAudioFileStreamProperty_FileFormat, &fileFormatSize, &fileFormat); - - break; - } - case kAudioFileStreamProperty_DataFormat: - { - AudioStreamBasicDescription newBasicDescription; - STKQueueEntry* entryToUpdate = currentlyReadingEntry; - - if (currentlyReadingEntry->audioStreamBasicDescription.mSampleRate == 0) - { - UInt32 size = sizeof(newBasicDescription); - - AudioFileStreamGetProperty(inAudioFileStream, kAudioFileStreamProperty_DataFormat, &size, &newBasicDescription); - - pthread_mutex_lock(&playerMutex); - - BOOL cancel = NO; - - AudioQueueSetParameter(audioQueue, kAudioQueueParam_Volume, 1); - - if (currentlyReadingEntry->cancel) - { - cancel = YES; - } - else - { - if (currentlyReadingEntry != currentlyPlayingEntry && audioQueue && currentAudioStreamBasicDescription.mSampleRate != 0) - { - if (memcmp(¤tAudioStreamBasicDescription, &newBasicDescription, sizeof(currentAudioStreamBasicDescription)) != 0) - { - cancel = YES; - } - } - } - - if (cancel) - { - [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); - } - - 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; - } - - [entryToUpdate updateAudioDataSource]; - - pthread_mutex_unlock(&playerMutex); - } - - break; - } - case kAudioFileStreamProperty_AudioDataByteCount: - { - UInt64 audioDataByteCount; - UInt32 byteCountSize = sizeof(audioDataByteCount); - - AudioFileStreamGetProperty(inAudioFileStream, kAudioFileStreamProperty_AudioDataByteCount, &byteCountSize, &audioDataByteCount); - - currentlyReadingEntry->audioDataByteCount = audioDataByteCount; - - [currentlyReadingEntry updateAudioDataSource]; - - break; - } - case kAudioFileStreamProperty_ReadyToProducePackets: - { - discontinuous = YES; - - break; - } - case kAudioFileStreamProperty_FormatList: - { - Boolean outWriteable; - UInt32 formatListSize; - OSStatus err = AudioFileStreamGetPropertyInfo(inAudioFileStream, kAudioFileStreamProperty_FormatList, &formatListSize, &outWriteable); - - if (err) - { - break; - } - - AudioFormatListItem* formatList = malloc(formatListSize); - - err = AudioFileStreamGetProperty(inAudioFileStream, kAudioFileStreamProperty_FormatList, &formatListSize, formatList); - - if (err) - { - free(formatList); - break; - } - - for (int i = 0; i * sizeof(AudioFormatListItem) < formatListSize; i += sizeof(AudioFormatListItem)) - { - AudioStreamBasicDescription pasbd = formatList[i].mASBD; - - if (pasbd.mFormatID == kAudioFormatMPEG4AAC_HE || pasbd.mFormatID == kAudioFormatMPEG4AAC_HE_V2) - { - // - // We've found HE-AAC, remember this to tell the audio queue - // when we construct it. - // -#if !TARGET_IPHONE_SIMULATOR - currentlyReadingEntry->audioStreamBasicDescription = pasbd; -#endif - break; - } - } - - free(formatList); - - break; - } - - } -} - --(void) handleAudioPackets:(const void*)inputData numberBytes:(UInt32)numberBytes numberPackets:(UInt32)numberPackets packetDescriptions:(AudioStreamPacketDescription*)packetDescriptionsIn -{ - if (currentlyReadingEntry == nil) - { - return; - } - - if (seekToTimeWasRequested || disposeWasRequested) - { - return; - } - - if (audioQueue == nil) - { - if (currentlyReadingEntry == currentlyPlayingEntry && currentlyReadingEntry != nil) - { - [self createAudioQueue]; - - if (audioQueue == nil) - { - return; - } - - if (self.internalState == AudioPlayerInternalStateStopped) - { - if (stopReason == AudioPlayerStopReasonEof) - { - stopReason = AudioPlayerStopReasonNoStop; - self.internalState = AudioPlayerInternalStateWaitingForData; - } - else - { - return; - } - } - } - } - else if (memcmp(¤tAudioStreamBasicDescription, ¤tlyReadingEntry->audioStreamBasicDescription, sizeof(currentAudioStreamBasicDescription)) != 0) - { - if (currentlyReadingEntry == currentlyPlayingEntry && currentlyReadingEntry != nil) - { - [self createAudioQueue]; - - if (audioQueue == nil) - { - return; - } - } - else - { - return; - } - } - - if (discontinuous) - { - discontinuous = NO; - } - - if (packetDescriptionsIn) - { - // VBR - - for (int i = 0; i < numberPackets; i++) - { - SInt64 packetOffset = packetDescriptionsIn[i].mStartOffset; - SInt64 packetSize = packetDescriptionsIn[i].mDataByteSize; - int bufSpaceRemaining; - - int framesPerPacket; - - if (packetDescriptionsIn[i].mVariableFramesInPacket > 0) - { - framesPerPacket = packetDescriptionsIn[i].mVariableFramesInPacket; - } - else - { - framesPerPacket = currentlyReadingEntry->audioStreamBasicDescription.mFramesPerPacket; - } - - if (currentlyReadingEntry->processedPacketsCount * framesPerPacket < currentlyReadingEntry->audioStreamBasicDescription.mSampleRate * 5 * currentlyReadingEntry->audioStreamBasicDescription.mChannelsPerFrame) - { - OSAtomicAdd32((int32_t)packetSize, ¤tlyReadingEntry->processedPacketsSizeTotal); - OSAtomicIncrement32(¤tlyReadingEntry->processedPacketsCount); - } - - if (packetSize > currentlyReadingEntry->packetBufferSize) - { - return; - } - - bufSpaceRemaining = currentlyReadingEntry->packetBufferSize - bytesFilled; - - if (bufSpaceRemaining < packetSize) - { - [self enqueueBuffer]; - - if (disposeWasRequested || seekToTimeWasRequested || self.internalState == AudioPlayerInternalStateStopped || self.internalState == AudioPlayerInternalStateStopping || self.internalState == AudioPlayerInternalStateDisposed) - { - return; - } - } - - if (bytesFilled + packetSize > currentlyReadingEntry->packetBufferSize) - { - return; - } - - AudioQueueBufferRef bufferToFill = audioQueueBuffer[fillBufferIndex]; - memcpy((char*)bufferToFill->mAudioData + bytesFilled, (const char*)inputData + packetOffset, (unsigned long)packetSize); - - framesFilled += framesPerPacket; - - packetDescs[packetsFilled] = packetDescriptionsIn[i]; - packetDescs[packetsFilled].mStartOffset = bytesFilled; - - bytesFilled += packetSize; - packetsFilled++; - - int packetsDescRemaining = audioQueueBufferCount - packetsFilled; - - if (packetsDescRemaining <= 0) - { - [self enqueueBuffer]; - - if (disposeWasRequested || seekToTimeWasRequested || self.internalState == AudioPlayerInternalStateStopped || self.internalState == AudioPlayerInternalStateStopping || self.internalState == AudioPlayerInternalStateDisposed) - { - return; - } - } - } - } - else - { - // CBR - - int offset = 0; - - while (numberBytes) - { - int bytesLeft = currentlyReadingEntry->packetBufferSize - bytesFilled; - - if (bytesLeft < numberBytes) - { - [self enqueueBuffer]; - - if (disposeWasRequested || seekToTimeWasRequested || self.internalState == AudioPlayerInternalStateStopped || self.internalState == AudioPlayerInternalStateStopping || self.internalState == AudioPlayerInternalStateDisposed) - { - return; - } - } - - int copySize; - bytesLeft = currentlyReadingEntry->packetBufferSize - bytesFilled; - - if (bytesLeft < numberBytes) - { - copySize = bytesLeft; - } - else - { - copySize = numberBytes; - } - - if (bytesFilled > currentlyPlayingEntry->packetBufferSize) - { - pthread_mutex_unlock(&playerMutex); - - return; - } - - AudioQueueBufferRef fillBuf = audioQueueBuffer[fillBufferIndex]; - memcpy((char*)fillBuf->mAudioData + bytesFilled, (const char*)(inputData + offset), copySize); - - bytesFilled += copySize; - packetsFilled = 0; - numberBytes -= copySize; - offset += copySize; - } - } -} - --(void) handleAudioQueueOutput:(AudioQueueRef)audioQueueIn buffer:(AudioQueueBufferRef)bufferIn -{ - int bufferIndex = -1; - - if (audioQueueIn != audioQueue) - { - return; - } - - STKQueueEntry* entry = nil; - - if (currentlyPlayingEntry) - { - OSSpinLockLock(¤tEntryReferencesLock); - { - if (currentlyPlayingEntry) - { - entry = currentlyPlayingEntry; - - if (!audioQueueFlushing) - { - entry.bytesBuffered += bufferIn->mAudioDataByteSize; - } - } - } - OSSpinLockUnlock(¤tEntryReferencesLock); - } - - int index = (int)bufferIn % audioQueueBufferRefLookupCount; - - for (int i = 0; i < audioQueueBufferCount; i++) - { - if (audioQueueBufferLookup[index].ref == bufferIn) - { - bufferIndex = audioQueueBufferLookup[index].bufferIndex; - - break; - } - - index = (index + 1) % audioQueueBufferRefLookupCount; - } - - audioPacketsPlayedCount++; - - if (bufferIndex == -1) - { - [self playbackThreadQueueMainThreadSyncBlock:^ - { - [self didEncounterError:AudioPlayerErrorUnknownBuffer]; - }]; - - pthread_mutex_lock(&queueBuffersMutex); - pthread_cond_signal(&queueBufferReadyCondition); - pthread_mutex_unlock(&queueBuffersMutex); - - return; - } - - pthread_mutex_lock(&queueBuffersMutex); - - BOOL signal = NO; - - if (bufferUsed[bufferIndex]) - { - bufferUsed[bufferIndex] = false; - numberOfBuffersUsed--; - } - else - { - // This should never happen - - signal = YES; - } - - if (entry != nil) - { - if (([self currentTimeInFrames] > entry.lastFrameIndex && entry.lastFrameIndex != -1)) - { - LOGINFO(@"Final frame played"); - - entry.lastFrameIndex = -1; - - [self invokeOnPlaybackThread:^ - { - [self audioQueueFinishedPlaying:entry]; - }]; - - signal = YES; - } - else if (entry.lastByteIndex == audioPacketsPlayedCount && entry.lastByteIndex != -1) - { - LOGINFO(@"Final AudioBuffer returned"); - - [self invokeOnPlaybackThread:^ - { - pthread_mutex_lock(&playerMutex); - - BOOL upcomingExistsAndIsIncompatible = NO; - STKQueueEntry* upcoming = [upcomingQueue peek]; - - if (upcoming != nil) - { - upcomingExistsAndIsIncompatible = [upcoming couldBeIncompatible:¤tAudioStreamBasicDescription]; - } - - BOOL nextBufferingExistsAndIsIncompatible = NO; - STKQueueEntry* nextBuffering = [bufferingQueue peek]; - - if (nextBuffering != nil) - { - if ([nextBuffering couldBeIncompatible:¤tAudioStreamBasicDescription]) - { - nextBufferingExistsAndIsIncompatible = YES; - nextBuffering->cancel = YES; - - NSAssert(bufferingQueue.count == 1, @"bufferingQueue.count == 1"); - } - } - - if ((upcoming == nil || upcomingExistsAndIsIncompatible) && (nextBuffering == nil || nextBufferingExistsAndIsIncompatible)) - { - if (self.internalState != AudioPlayerInternalStateFlushingAndStoppingButStillPlaying) - { - if (audioQueue && [self audioQueueIsRunning]) - { - self.internalState = AudioPlayerInternalStateFlushingAndStoppingButStillPlaying; - - LOGINFO(@"AudioQueueStop from handleAudioQueueOutput"); - - AudioQueueStop(audioQueue, NO); - } - } - } - - pthread_mutex_unlock(&playerMutex); - }]; - } - } - - if (!audioQueueFlushing) - { - if (numberOfBuffersUsed == 0 - && !seekToTimeWasRequested - && !disposeWasRequested - && self.internalState != AudioPlayerInternalStateWaitingForData) - { - if (self->rebufferingStartFrames == 0) - { - [self invokeOnPlaybackThread:^ - { - pthread_mutex_lock(&playerMutex); - - if (self->numberOfBuffersUsed == 0 - && !disposeWasRequested - && !seekToTimeWasRequested - && self->rebufferingStartFrames == 0 - && self.internalState != AudioPlayerInternalStateWaitingForData - && [self moreFramesAreDefinitelyAvailableToPlay]) - { - if ([self moreFramesAreDefinitelyAvailableToPlay]) - { - } - - AudioQueuePause(audioQueue); - - self->rebufferingStartFrames = [self currentTimeInFrames]; - - LOGINFO(([NSString stringWithFormat:@"Buffer underrun with time: %f", [self currentTimeInFrames]])); - } - - pthread_mutex_unlock(&playerMutex); - }]; - } - - if (self.internalState != AudioPlayerInternalStateRebuffering && self.internalState != AudioPlayerInternalStatePaused) - { - Float64 interval = STK_FRAMES_MISSED_BEFORE_CONSIDERED_UNDERRUN / currentAudioStreamBasicDescription.mSampleRate; - - [self invokeOnPlaybackThreadAtInterval:interval withBlock:^ - { - [self setRebufferingStateIfApplicable]; - }]; - } - - signal = YES; - } - } - - signal = signal || disposeWasRequested; - - if (self.internalState == AudioPlayerInternalStateStopped - || self.internalState == AudioPlayerInternalStateStopping - || self.internalState == AudioPlayerInternalStateDisposed - || self.internalState == AudioPlayerInternalStateError) - { - signal = signal || waiting || numberOfBuffersUsed < (STK_BUFFERS_NEEDED_TO_START * 2); - } - else if (audioQueueFlushing) - { - signal = signal || (audioQueueFlushing && numberOfBuffersUsed < (STK_BUFFERS_NEEDED_TO_START * 2)); - } - else - { - signal = signal || seekToTimeWasRequested || (numberOfBuffersUsed < self->audioQueueBufferCount / 2); - } - - if (signal) - { - pthread_cond_signal(&queueBufferReadyCondition); - } - - 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; - } - - return NO; -} - --(Float64) currentTimeInFrames -{ - if (audioQueue == nil) - { - return 0; - } - - AudioTimeStamp timeStamp; - Boolean outTimelineDiscontinuity; - - AudioQueueGetCurrentTime(audioQueue, NULL, &timeStamp, &outTimelineDiscontinuity); - - return timeStamp.mSampleTime - timelineAdjust; -} - --(void) processFinishedPlayingViaAudioQueueStop -{ - if (currentlyPlayingEntry) - { - pthread_mutex_lock(&playerMutex); - - STKQueueEntry* entry = currentlyPlayingEntry; - - entry.lastFrameIndex = -1; - - pthread_mutex_unlock(&playerMutex); - - [self invokeOnPlaybackThread:^ - { - self->stopReason = AudioPlayerStopReasonEof; - self.internalState = AudioPlayerInternalStateStopped; - - if (audioQueue) - { - [self stopAudioQueueWithReason:@"processFinishedPlayingViaAudioQueueStop"]; - } - - [self audioQueueFinishedPlaying:entry]; - }]; - } - else - { - pthread_mutex_lock(&playerMutex); - - STKQueueEntry* entry = currentlyPlayingEntry; - - entry.lastFrameIndex = -1; - - pthread_mutex_unlock(&playerMutex); - - [self invokeOnPlaybackThread:^ - { - if (audioQueue) - { - [self stopAudioQueueWithReason:@"processFinishedPlayingViaAudioQueueStop"]; - } - - [self audioQueueFinishedPlaying:entry]; - }]; - } -} - --(void) handlePropertyChangeForQueue:(AudioQueueRef)audioQueueIn propertyID:(AudioQueuePropertyID)propertyId -{ - if (audioQueueIn != audioQueue) - { - return; - } - - if (propertyId == kAudioQueueProperty_IsRunning) - { - if (![self audioQueueIsRunning] && self.internalState == AudioPlayerInternalStateStopping) - { - self.internalState = AudioPlayerInternalStateStopped; - } - else if (![self audioQueueIsRunning] && self.internalState == AudioPlayerInternalStateFlushingAndStoppingButStillPlaying) - { - LOGINFO(@"AudioQueue not IsRunning") - - [self invokeOnPlaybackThread:^ - { - [self processFinishedPlayingViaAudioQueueStop]; - }]; - - pthread_mutex_lock(&queueBuffersMutex); - - if (signal) - { - pthread_cond_signal(&queueBufferReadyCondition); - } - - pthread_mutex_unlock(&queueBuffersMutex); - } - } -} - --(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 || self.internalState == AudioPlayerInternalStateWaitingForDataAfterSeek) && (numberOfBuffersUsed >= STK_BUFFERS_NEEDED_TO_START || tailEndOfBuffer); -} - --(void) enqueueBuffer -{ - pthread_mutex_lock(&playerMutex); - { - OSStatus error; - - if (audioFileStream == 0) - { - pthread_mutex_unlock(&playerMutex); - - return; - } - - if (self.internalState == AudioPlayerInternalStateStopped) - { - pthread_mutex_unlock(&playerMutex); - - return; - } - - if (audioQueueFlushing || newFileToPlay) - { - pthread_mutex_unlock(&playerMutex); - - return; - } - - pthread_mutex_lock(&queueBuffersMutex); - - bufferUsed[fillBufferIndex] = true; - numberOfBuffersUsed++; - - AudioQueueBufferRef buffer = audioQueueBuffer[fillBufferIndex]; - - buffer->mAudioDataByteSize = bytesFilled; - - if (packetsFilled) - { - error = AudioQueueEnqueueBuffer(audioQueue, buffer, packetsFilled, packetDescs); - } - else - { - error = AudioQueueEnqueueBuffer(audioQueue, buffer, 0, NULL); - } - - audioPacketsReadCount++; - framesQueued += framesFilled; - - pthread_mutex_unlock(&queueBuffersMutex); - - if (error) - { - [self stopAudioQueueWithReason:@"enqueueBuffer critical error"]; - - pthread_mutex_unlock(&playerMutex); - - return; - } - - OSSpinLockLock(¤tEntryReferencesLock); - - if ([self readyToEndRebufferingState]) - { - OSSpinLockUnlock(¤tEntryReferencesLock); - - if (self.internalState != AudioPlayerInternalStatePaused) - { - self->rebufferingStartFrames = 0; - - if (![self startAudioQueue] || audioQueue == nil) - { - pthread_mutex_unlock(&playerMutex); - - return; - } - } - else - { - OSSpinLockUnlock(¤tEntryReferencesLock); - } - } - else - { - OSSpinLockUnlock(¤tEntryReferencesLock); - } - - if ([self readyToEndWaitingForDataState]) - { - if (self.internalState != AudioPlayerInternalStatePaused) - { - if (![self startAudioQueue] || audioQueue == nil) - { - pthread_mutex_unlock(&playerMutex); - - return; - } - } - } - - if (++fillBufferIndex >= audioQueueBufferCount) - { - fillBufferIndex = 0; - } - - bytesFilled = 0; - framesFilled = 0; - packetsFilled = 0; - } - pthread_mutex_unlock(&playerMutex); - - pthread_mutex_lock(&queueBuffersMutex); - - waiting = YES; - - while (bufferUsed[fillBufferIndex] && !(disposeWasRequested || seekToTimeWasRequested || self.internalState == AudioPlayerInternalStateStopped || self.internalState == AudioPlayerInternalStateStopping || self.internalState == AudioPlayerInternalStateDisposed)) - { - 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 = errorCodeIn; - self.internalState = AudioPlayerInternalStateError; - - [self playbackThreadQueueMainThreadSyncBlock:^ - { - [self.delegate audioPlayer:self didEncounterError:errorCode]; - }]; -} - --(void) createAudioQueue -{ - OSStatus error; - - LOGINFO(@"Called"); - - [self startSystemBackgroundTask]; - - pthread_mutex_lock(&playerMutex); - pthread_mutex_lock(&queueBuffersMutex); - - if (audioQueue) - { - LOGINFO(@"AudioQueueStop/1"); - - pthread_mutex_unlock(&queueBuffersMutex); - AudioQueueStop(audioQueue, YES); - AudioQueueDispose(audioQueue, YES); - - memset(¤tAudioStreamBasicDescription, 0, sizeof(currentAudioStreamBasicDescription)); - - audioQueue = nil; - - pthread_mutex_lock(&queueBuffersMutex); - } - - if (currentlyPlayingEntry == nil) - { - LOGINFO(@"currentlyPlayingEntry == nil"); - - pthread_mutex_unlock(&queueBuffersMutex); - pthread_mutex_unlock(&playerMutex); - - return; - } - - currentAudioStreamBasicDescription = currentlyPlayingEntry->audioStreamBasicDescription; - - error = AudioQueueNewOutput(¤tAudioStreamBasicDescription, AudioQueueOutputCallbackProc, (__bridge void*)self, NULL, NULL, 0, &audioQueue); - - if (error) - { - [self playbackThreadQueueMainThreadSyncBlock:^ - { - [self.delegate audioPlayer:self didEncounterError:AudioPlayerErrorQueueCreationFailed]; - }]; - - pthread_mutex_unlock(&queueBuffersMutex); - pthread_mutex_unlock(&playerMutex); - - return; - } - - AudioQueuePause(audioQueue); - - error = AudioQueueAddPropertyListener(audioQueue, kAudioQueueProperty_IsRunning, AudioQueueIsRunningCallbackProc, (__bridge void*)self); - - if (error) - { - [self playbackThreadQueueMainThreadSyncBlock:^ - { - [self.delegate audioPlayer:self didEncounterError:AudioPlayerErrorQueueCreationFailed]; - }]; - - pthread_mutex_unlock(&queueBuffersMutex); - pthread_mutex_unlock(&playerMutex); - - return; - } - -#if TARGET_OS_IPHONE - UInt32 val = kAudioQueueHardwareCodecPolicy_PreferHardware; - - AudioQueueSetProperty(audioQueue, kAudioQueueProperty_HardwareCodecPolicy, &val, sizeof(UInt32)); - - AudioQueueSetParameter(audioQueue, kAudioQueueParam_Volume, 1); -#endif - - memset(audioQueueBufferLookup, 0, sizeof(AudioQueueBufferRefLookupEntry) * audioQueueBufferRefLookupCount); - - // Allocate AudioQueue buffers - - for (int i = 0; i < audioQueueBufferCount; i++) - { - error = AudioQueueAllocateBuffer(audioQueue, currentlyPlayingEntry->packetBufferSize, &audioQueueBuffer[i]); - - unsigned int hash = (unsigned int)audioQueueBuffer[i] % audioQueueBufferRefLookupCount; - - while (true) - { - if (audioQueueBufferLookup[hash].ref == 0) - { - audioQueueBufferLookup[hash].ref = audioQueueBuffer[i]; - audioQueueBufferLookup[hash].bufferIndex = i; - - break; - } - else - { - hash++; - hash %= audioQueueBufferRefLookupCount; - } - } - - bufferUsed[i] = false; - - if (error) - { - [self playbackThreadQueueMainThreadSyncBlock:^ - { - [self.delegate audioPlayer:self didEncounterError:AudioPlayerErrorQueueCreationFailed]; - }]; - - pthread_mutex_unlock(&playerMutex); - - return; - } - } - - audioPacketsReadCount = 0; - audioPacketsPlayedCount = 0; - framesQueued = 0; - timelineAdjust = 0; - rebufferingStartFrames = 0; - - // Get file cookie/magic bytes information - - UInt32 cookieSize; - Boolean writable; - - error = AudioFileStreamGetPropertyInfo(audioFileStream, kAudioFileStreamProperty_MagicCookieData, &cookieSize, &writable); - - if (!error) - { - void* cookieData = calloc(1, cookieSize); - - error = AudioFileStreamGetProperty(audioFileStream, kAudioFileStreamProperty_MagicCookieData, &cookieSize, cookieData); - - if (error) - { - free(cookieData); - - pthread_mutex_unlock(&queueBuffersMutex); - pthread_mutex_unlock(&playerMutex); - - return; - } - - error = AudioQueueSetProperty(audioQueue, kAudioQueueProperty_MagicCookie, cookieData, cookieSize); - - if (error) - { - free(cookieData); - - [self playbackThreadQueueMainThreadSyncBlock:^ - { - [self.delegate audioPlayer:self didEncounterError:AudioPlayerErrorQueueCreationFailed]; - }]; - - pthread_mutex_unlock(&queueBuffersMutex); - pthread_mutex_unlock(&playerMutex); - - return; - } - - free(cookieData); - } - - AudioQueueSetParameter(audioQueue, kAudioQueueParam_Volume, 1); - - // Reset metering enabled in case the user set it before the queue was created - - [self setMeteringEnabled:meteringEnabled]; - - pthread_mutex_unlock(&queueBuffersMutex); - pthread_mutex_unlock(&playerMutex); -} - --(double) duration -{ - if (newFileToPlay) - { - return 0; - } - - OSSpinLockLock(¤tEntryReferencesLock); - - STKQueueEntry* entry = currentlyPlayingEntry; - - if (entry == nil) - { - OSSpinLockUnlock(¤tEntryReferencesLock); - - return 0; - } - - double retval = [entry duration]; - - OSSpinLockUnlock(¤tEntryReferencesLock); - - return retval; -} - --(double) progress -{ - if (seekToTimeWasRequested) - { - return requestedSeekTime; - } - - if (newFileToPlay) - { - return 0; - } - - OSSpinLockLock(¤tEntryReferencesLock); - - Float64 currentTime = [self currentTimeInFrames]; - STKQueueEntry* entry = currentlyPlayingEntry; - - if (entry == nil) - { - OSSpinLockUnlock(¤tEntryReferencesLock); - - return 0; - } - - Float64 time = currentTime; - - double retval = [entry calculateProgressWithTotalFramesPlayed:time]; - - OSSpinLockUnlock(¤tEntryReferencesLock); - - return retval; -} - --(BOOL) invokeOnPlaybackThread:(void(^)())block -{ - NSRunLoop* runLoop = playbackThreadRunLoop; - - if (runLoop) - { - CFRunLoopPerformBlock([runLoop getCFRunLoop], NSRunLoopCommonModes, block); - CFRunLoopWakeUp([runLoop getCFRunLoop]); - - return YES; - } - - 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:^ - { - [self processRunloop]; - }]; - - pthread_mutex_lock(&queueBuffersMutex); - - if (waiting) - { - pthread_cond_signal(&queueBufferReadyCondition); - } - - pthread_mutex_unlock(&queueBuffersMutex); -} - --(void) seekToTime:(double)value -{ - if (currentlyPlayingEntry == nil) - { - return; - } - - OSSpinLockLock(&seekLock); - - BOOL seekAlreadyRequested = seekToTimeWasRequested; - - seekToTimeWasRequested = YES; - requestedSeekTime = value; - - if (!seekAlreadyRequested) - { - OSAtomicIncrement32(&seekVersion); - - OSSpinLockUnlock(&seekLock); - - [self wakeupPlaybackThread]; - - return; - } - - OSSpinLockUnlock(&seekLock); -} - --(void) createOrWakeupPlaybackThread -{ - if (playbackThread == nil) - { - newFileToPlay = YES; - - playbackThread = [[NSThread alloc] initWithTarget:self selector:@selector(startInternal) object:nil]; - - [playbackThread start]; - - [self wakeupPlaybackThread]; - } - else - { - [self wakeupPlaybackThread]; - } -} - --(void) setCurrentlyReadingEntry:(STKQueueEntry*)entry andStartPlaying:(BOOL)startPlaying -{ - LOGINFO(([entry description])); - - if (startPlaying) - { - if (audioQueue) - { - [self resetAudioQueueWithReason:@"from setCurrentlyReadingEntry" andPause:YES]; - } - } - - if (audioFileStream) - { - AudioFileStreamClose(audioFileStream); - - audioFileStream = 0; - } - - if (currentlyReadingEntry) - { - currentlyReadingEntry.dataSource.delegate = nil; - [currentlyReadingEntry.dataSource unregisterForEvents]; - [currentlyReadingEntry.dataSource close]; - } - - OSSpinLockLock(¤tEntryReferencesLock); - currentlyReadingEntry = entry; - OSSpinLockUnlock(¤tEntryReferencesLock); - - currentlyReadingEntry.dataSource.delegate = self; - - - - [currentlyReadingEntry.dataSource registerForEvents:[NSRunLoop currentRunLoop]]; - [currentlyReadingEntry.dataSource seekToOffset:0]; - - if (startPlaying) - { - [self clearQueueIncludingUpcoming:NO]; - - [self processFinishPlayingIfAnyAndPlayingNext:currentlyPlayingEntry withNext:entry]; - } - else - { - [bufferingQueue enqueue:entry]; - } -} - --(void) audioQueueFinishedPlaying:(STKQueueEntry*)entry -{ - pthread_mutex_lock(&playerMutex); - { - STKQueueEntry* next = [bufferingQueue peek]; - - if (next == nil) - { - [self processRunloop]; - - next = [bufferingQueue peek]; - } - - next = [bufferingQueue dequeue]; - - [self processFinishPlayingIfAnyAndPlayingNext:entry withNext:next]; - - [self processRunloop]; - } - pthread_mutex_unlock(&playerMutex); -} - --(void) processFinishPlayingIfAnyAndPlayingNext:(STKQueueEntry*)entry withNext:(STKQueueEntry*)next -{ - if (entry != currentlyPlayingEntry) - { - return; - } - - LOGINFO(([NSString stringWithFormat:@"Finished: %@, Next: %@, buffering.count=%d,upcoming.count=%d", entry ? [entry description] : @"nothing", [next description], (int)bufferingQueue.count, (int)upcomingQueue.count])); - - NSObject* queueItemId = entry.queueItemId; - double progress = [entry calculateProgressWithTotalFramesPlayed:[self currentTimeInFrames]]; - double duration = [entry duration]; - - BOOL isPlayingSameItemProbablySeek = currentlyPlayingEntry == next; - - if (next) - { - if (!isPlayingSameItemProbablySeek) - { - next.seekTime = 0; - - OSSpinLockLock(&seekLock); - seekToTimeWasRequested = NO; - OSSpinLockUnlock(&seekLock); - } - - OSSpinLockLock(¤tEntryReferencesLock); - currentlyPlayingEntry = next; - currentlyPlayingEntry.bytesBuffered = 0; - currentlyPlayingEntry.firstFrameIndex = [self currentTimeInFrames]; - NSObject* playingQueueItemId = playingQueueItemId = currentlyPlayingEntry.queueItemId; - OSSpinLockUnlock(¤tEntryReferencesLock); - - if (!isPlayingSameItemProbablySeek && entry) - { - [self playbackThreadQueueMainThreadSyncBlock:^ - { - [self.delegate audioPlayer:self didFinishPlayingQueueItemId:queueItemId withReason:stopReason andProgress:progress andDuration:duration]; - }]; - } - - if (!isPlayingSameItemProbablySeek) - { - [self playbackThreadQueueMainThreadSyncBlock:^ - { - [self.delegate audioPlayer:self didStartPlayingQueueItemId:playingQueueItemId]; - }]; - } - } - else - { - OSSpinLockLock(¤tEntryReferencesLock); - currentlyPlayingEntry = nil; - OSSpinLockUnlock(¤tEntryReferencesLock); - - if (currentlyReadingEntry == nil) - { - if (upcomingQueue.count == 0) - { - stopReason = AudioPlayerStopReasonEof; - self.internalState = AudioPlayerInternalStateStopping; - } - } - - if (!isPlayingSameItemProbablySeek && entry) - { - [self playbackThreadQueueMainThreadSyncBlock:^ - { - [self.delegate audioPlayer:self didFinishPlayingQueueItemId:queueItemId withReason:stopReason andProgress:progress andDuration:duration]; - }]; - } - } -} - --(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 -{ - block = [block copy]; - - [self invokeOnPlaybackThread:^ - { - if (disposeWasRequested) - { - return; - } - - [self dispatchSyncOnMainThread:block]; - }]; -} - --(BOOL) processRunloop -{ - BOOL dontPlayNew = NO; - - pthread_mutex_lock(&playerMutex); - { - dontPlayNew = self.internalState == AudioPlayerInternalStateFlushingAndStoppingButStillPlaying; - - if (self.internalState == AudioPlayerInternalStatePaused) - { - pthread_mutex_unlock(&playerMutex); - - return YES; - } - else if (newFileToPlay) - { - STKQueueEntry* entry = [upcomingQueue dequeue]; - - self.internalState = AudioPlayerInternalStateWaitingForData; - - [self setCurrentlyReadingEntry:entry andStartPlaying:YES]; - - newFileToPlay = NO; - } - else if (seekToTimeWasRequested && currentlyPlayingEntry && currentlyPlayingEntry != currentlyReadingEntry) - { - currentlyPlayingEntry.lastFrameIndex = -1; - currentlyPlayingEntry.lastByteIndex = -1; - - self.internalState = AudioPlayerInternalStateWaitingForDataAfterSeek; - - [self setCurrentlyReadingEntry:currentlyPlayingEntry andStartPlaying:YES]; - - currentlyReadingEntry->parsedHeader = NO; - } - else if (self.internalState == AudioPlayerInternalStateStopped && stopReason == AudioPlayerStopReasonUserAction) - { - [self stopAudioQueueWithReason:@"from processRunLoop/1"]; - - currentlyReadingEntry.dataSource.delegate = nil; - [currentlyReadingEntry.dataSource unregisterForEvents]; - [currentlyReadingEntry.dataSource close]; - - if (currentlyPlayingEntry) - { - [self processFinishPlayingIfAnyAndPlayingNext:currentlyPlayingEntry withNext:nil]; - } - - if ([bufferingQueue peek] == currentlyPlayingEntry) - { - [bufferingQueue dequeue]; - } - - OSSpinLockLock(¤tEntryReferencesLock); - currentlyPlayingEntry = nil; - currentlyReadingEntry = nil; - seekToTimeWasRequested = NO; - OSSpinLockUnlock(¤tEntryReferencesLock); - } - else if (self.internalState == AudioPlayerInternalStateStopped && stopReason == AudioPlayerStopReasonUserActionFlushStop) - { - currentlyReadingEntry.dataSource.delegate = nil; - [currentlyReadingEntry.dataSource unregisterForEvents]; - [currentlyReadingEntry.dataSource close]; - - if (currentlyPlayingEntry) - { - [self processFinishPlayingIfAnyAndPlayingNext:currentlyPlayingEntry withNext:nil]; - } - - if ([bufferingQueue peek] == currentlyPlayingEntry) - { - [bufferingQueue dequeue]; - } - - OSSpinLockLock(¤tEntryReferencesLock); - currentlyPlayingEntry = nil; - currentlyReadingEntry = nil; - OSSpinLockUnlock(¤tEntryReferencesLock); - - [self resetAudioQueueWithReason:@"from processRunLoop"]; - } - else if (currentlyReadingEntry == nil && !dontPlayNew) - { - STKQueueEntry* next = [bufferingQueue peek]; - - if (next != nil && next->audioStreamBasicDescription.mSampleRate == 0) - { - goto endOfIfElse; - } - - next = [upcomingQueue peek]; - - if ([next isKnownToBeIncompatible:¤tAudioStreamBasicDescription] && currentlyPlayingEntry != nil) - { - goto endOfIfElse; - } - - if (upcomingQueue.count > 0) - { - STKQueueEntry* entry = [upcomingQueue dequeue]; - - BOOL startPlaying = currentlyPlayingEntry == nil; - BOOL wasCurrentlyPlayingNothing = currentlyPlayingEntry == nil; - - [self setCurrentlyReadingEntry:entry andStartPlaying:startPlaying]; - - if (wasCurrentlyPlayingNothing) - { - currentAudioStreamBasicDescription.mSampleRate = 0; - - [self setInternalState:AudioPlayerInternalStateWaitingForData]; - } - } - else if (currentlyPlayingEntry == nil) - { - if (self.internalState != AudioPlayerInternalStateStopped) - { - [self stopAudioQueueWithReason:@"from processRunLoop/2"]; - stopReason = AudioPlayerStopReasonEof; - } - } - } - - endOfIfElse: - - if (disposeWasRequested) - { - pthread_mutex_unlock(&playerMutex); - - return NO; - } - - if (currentlyPlayingEntry && currentlyPlayingEntry->parsedHeader) - { - int32_t originalSeekVersion; - BOOL originalSeekToTimeRequested; - - OSSpinLockLock(&seekLock); - originalSeekVersion = seekVersion; - originalSeekToTimeRequested = seekToTimeWasRequested; - OSSpinLockUnlock(&seekLock); - - if (originalSeekToTimeRequested && currentlyReadingEntry == currentlyPlayingEntry) - { - [self processSeekToTime]; - - OSSpinLockLock(&seekLock); - if (originalSeekVersion == seekVersion) - { - seekToTimeWasRequested = NO; - } - OSSpinLockUnlock(&seekLock); - } - } - else if (currentlyPlayingEntry == nil && seekToTimeWasRequested) - { - seekToTimeWasRequested = NO; - } - } - pthread_mutex_unlock(&playerMutex); - - - return YES; -} - --(void) startInternal -{ - @autoreleasepool - { - playbackThreadRunLoop = [NSRunLoop currentRunLoop]; - NSThread.currentThread.threadPriority = 1; - - bytesFilled = 0; - packetsFilled = 0; - - [playbackThreadRunLoop addPort:[NSPort port] forMode:NSDefaultRunLoopMode]; - - while (true) - { - @autoreleasepool - { - if (![self processRunloop]) - { - break; - } - } - - NSDate* date = [[NSDate alloc] initWithTimeIntervalSinceNow:10]; - [playbackThreadRunLoop runMode:NSDefaultRunLoopMode beforeDate:date]; - } - - disposeWasRequested = NO; - seekToTimeWasRequested = NO; - - currentlyReadingEntry.dataSource.delegate = nil; - currentlyPlayingEntry.dataSource.delegate = nil; - - pthread_mutex_lock(&playerMutex); - OSSpinLockLock(¤tEntryReferencesLock); - currentlyPlayingEntry = nil; - currentlyReadingEntry = nil; - OSSpinLockUnlock(¤tEntryReferencesLock); - pthread_mutex_unlock(&playerMutex); - - self.internalState = AudioPlayerInternalStateDisposed; - - [threadFinishedCondLock lock]; - [threadFinishedCondLock unlockWithCondition:1]; - } -} - --(void) processSeekToTime -{ - OSStatus error; - OSSpinLockLock(¤tEntryReferencesLock); - STKQueueEntry* currentEntry = currentlyReadingEntry; - OSSpinLockUnlock(¤tEntryReferencesLock); - - NSAssert(currentEntry == currentlyPlayingEntry, @"playing and reading must be the same"); - - if (!currentEntry || ([currentEntry calculatedBitRate] == 0.0 || currentlyPlayingEntry.dataSource.length <= 0)) - { - return; - } - - long long seekByteOffset = currentEntry->audioDataOffset + (requestedSeekTime / self.duration) * (currentlyReadingEntry.audioDataLengthInBytes); - - if (seekByteOffset > currentEntry.dataSource.length - (2 * currentEntry->packetBufferSize)) - { - seekByteOffset = currentEntry.dataSource.length - 2 * currentEntry->packetBufferSize; - } - - currentEntry.seekTime = requestedSeekTime; - currentEntry->lastProgress = requestedSeekTime; - - double calculatedBitRate = [currentEntry calculatedBitRate]; - - if (currentEntry->packetDuration > 0 && calculatedBitRate > 0) - { - UInt32 ioFlags = 0; - SInt64 packetAlignedByteOffset; - SInt64 seekPacket = floor(requestedSeekTime / currentEntry->packetDuration); - - error = AudioFileStreamSeek(audioFileStream, seekPacket, &packetAlignedByteOffset, &ioFlags); - - if (!error && !(ioFlags & kAudioFileStreamSeekFlag_OffsetIsEstimated)) - { - double delta = ((seekByteOffset - (SInt64)currentEntry->audioDataOffset) - packetAlignedByteOffset) / calculatedBitRate * 8; - - currentEntry.seekTime -= delta; - - seekByteOffset = packetAlignedByteOffset + currentEntry->audioDataOffset; - } - } - - currentEntry.lastFrameIndex = -1; - [currentEntry updateAudioDataSource]; - [currentEntry.dataSource seekToOffset:seekByteOffset]; - - if (self.internalState == AudioPlayerInternalStateFlushingAndStoppingButStillPlaying) - { - self.internalState = AudioPlayerInternalStatePlaying; - } - - if (seekByteOffset > 0) - { - discontinuous = YES; - } - - if (audioQueue) - { - [self resetAudioQueueWithReason:@"from seekToTime"]; - } - - currentEntry.bytesBuffered = 0; - currentEntry.firstFrameIndex = [self currentTimeInFrames]; - - [self clearQueue]; -} - --(BOOL) startAudioQueue -{ - OSStatus error; - - LOGINFO(@"Called"); - - AudioQueueSetParameter(audioQueue, kAudioQueueParam_Volume, 1); - - error = AudioQueueStart(audioQueue, NULL); - - if (error) - { -#if TARGET_OS_IPHONE - if (backgroundTaskId == UIBackgroundTaskInvalid) - { - [self startSystemBackgroundTask]; - } -#endif - - [self stopAudioQueueWithReason:@"from startAudioQueue"]; - [self createAudioQueue]; - - if (audioQueue != nil) - { - AudioQueueStart(audioQueue, NULL); - } - } - - [self stopSystemBackgroundTask]; - - self.internalState = AudioPlayerInternalStatePlaying; - - return YES; -} - --(void) stopAudioQueueWithReason:(NSString*)reason -{ - OSStatus error; - - LOGINFO(([NSString stringWithFormat:@"With Reason: %@", reason])); - - if (!audioQueue) - { - LOGINFO(@"Already No AudioQueue"); - - self.internalState = AudioPlayerInternalStateStopped; - - return; - } - else - { - LOGINFO(@"Stopping AudioQueue"); - - audioQueueFlushing = YES; - - error = AudioQueueStop(audioQueue, YES); - error = error | AudioQueueDispose(audioQueue, YES); - - memset(¤tAudioStreamBasicDescription, 0, sizeof(currentAudioStreamBasicDescription)); - - audioQueue = nil; - } - - if (error) - { - [self didEncounterError:AudioPlayerErrorQueueStopFailed]; - } - - pthread_mutex_lock(&queueBuffersMutex); - - if (numberOfBuffersUsed != 0) - { - numberOfBuffersUsed = 0; - - memset(&bufferUsed[0], 0, sizeof(bool) * audioQueueBufferCount); - } - - pthread_cond_signal(&queueBufferReadyCondition); - pthread_mutex_unlock(&queueBuffersMutex); - - bytesFilled = 0; - fillBufferIndex = 0; - packetsFilled = 0; - framesQueued = 0; - timelineAdjust = 0; - rebufferingStartFrames = 0; - - audioPacketsReadCount = 0; - audioPacketsPlayedCount = 0; - audioQueueFlushing = NO; - - self.internalState = AudioPlayerInternalStateStopped; -} - - --(void) resetAudioQueueWithReason:(NSString*)reason -{ - [self resetAudioQueueWithReason:reason andPause:NO]; -} - --(void) resetAudioQueueWithReason:(NSString*)reason andPause:(BOOL)pause -{ - OSStatus error; - - LOGINFO(([NSString stringWithFormat:@"With Reason: %@", reason])); - - pthread_mutex_lock(&playerMutex); - { - audioQueueFlushing = YES; - - if (audioQueue) - { - AudioTimeStamp timeStamp; - Boolean outTimelineDiscontinuity; - - error = AudioQueueReset(audioQueue); - - if (pause) - { - AudioQueuePause(audioQueue); - } - - AudioQueueGetCurrentTime(audioQueue, NULL, &timeStamp, &outTimelineDiscontinuity); - - timelineAdjust = timeStamp.mSampleTime; - - BOOL startAudioQueue = NO; - - if (rebufferingStartFrames > 0) - { - startAudioQueue = YES; - rebufferingStartFrames = 0; - } - - if (!pause && startAudioQueue) - { - [self startAudioQueue]; - } - - if (error) - { - [self playbackThreadQueueMainThreadSyncBlock:^ - { - [self didEncounterError:AudioPlayerErrorQueueStopFailed];; - }]; - } - } - } - pthread_mutex_unlock(&playerMutex); - - pthread_mutex_lock(&queueBuffersMutex); - - if (numberOfBuffersUsed != 0) - { - numberOfBuffersUsed = 0; - - memset(&bufferUsed[0], 0, sizeof(bool) * audioQueueBufferCount); - } - - pthread_cond_signal(&queueBufferReadyCondition); - - - bytesFilled = 0; - fillBufferIndex = 0; - packetsFilled = 0; - framesQueued = 0; - - if (currentlyPlayingEntry) - { - currentlyPlayingEntry->lastProgress = 0; - } - - audioPacketsReadCount = 0; - audioPacketsPlayedCount = 0; - audioQueueFlushing = NO; - - pthread_mutex_unlock(&queueBuffersMutex); -} - --(void) dataSourceDataAvailable:(STKDataSource*)dataSourceIn -{ - OSStatus error; - - if (currentlyReadingEntry.dataSource != dataSourceIn) - { - return; - } - - if (!currentlyReadingEntry.dataSource.hasBytesAvailable) - { - return; - } - - int read = [currentlyReadingEntry.dataSource readIntoBuffer:readBuffer withSize:readBufferSize]; - - if (read == 0) - { - return; - } - - if (audioFileStream == 0) - { - error = AudioFileStreamOpen((__bridge void*)self, AudioFileStreamPropertyListenerProc, AudioFileStreamPacketsProc, dataSourceIn.audioFileTypeHint, &audioFileStream); - - if (error) - { - return; - } - } - - if (read < 0) - { - // iOS will shutdown network connections if the app is backgrounded (i.e. device is locked when player is paused) - // We try to reopen -- should probably add a back-off protocol in the future - - long long position = currentlyReadingEntry.dataSource.position; - - [currentlyReadingEntry.dataSource seekToOffset:position]; - - return; - } - - int flags = 0; - - if (discontinuous) - { - flags = kAudioFileStreamParseFlag_Discontinuity; - } - - if (audioFileStream) - { - error = AudioFileStreamParseBytes(audioFileStream, read, readBuffer, flags); - - if (error) - { - if (dataSourceIn == currentlyPlayingEntry.dataSource) - { - [self didEncounterError:AudioPlayerErrorStreamParseBytesFailed]; - } - - return; - } - - OSSpinLockLock(¤tEntryReferencesLock); - - if (currentlyReadingEntry == nil) - { - [dataSourceIn unregisterForEvents]; - [dataSourceIn close]; - } - - OSSpinLockUnlock(¤tEntryReferencesLock); - } -} - --(void) dataSourceErrorOccured:(STKDataSource*)dataSourceIn -{ - if (currentlyReadingEntry.dataSource != dataSourceIn) - { - return; - } - - [self didEncounterError:AudioPlayerErrorDataNotFound]; -} - --(void) dataSourceEof:(STKDataSource*)dataSourceIn -{ - if (currentlyReadingEntry.dataSource != dataSourceIn) - { - return; - } - - LOGINFO(([NSString stringWithFormat:@"eof: %lld %lld", audioPacketsReadCount, audioPacketsPlayedCount])); - - if (bytesFilled > 0) - { - LOGINFO(([NSString stringWithFormat:@"eof: enqueueBuffer"])); - - [self enqueueBuffer]; - } - - LOGINFO(([NSString stringWithFormat:@" %@ (ptr:%d)", dataSourceIn, (int)dataSourceIn])); - - NSObject* queueItemId = currentlyReadingEntry.queueItemId; - - if (disposeWasRequested) - { - return; - } - - [self dispatchSyncOnMainThread:^ - { - [self.delegate audioPlayer:self didFinishBufferingSourceWithQueueItemId:queueItemId]; - }]; - - if (disposeWasRequested) - { - return; - } - - pthread_mutex_lock(&playerMutex); - - if (audioQueue) - { - currentlyReadingEntry.lastFrameIndex = self->framesQueued; - currentlyReadingEntry.lastByteIndex = audioPacketsReadCount; - - LOGINFO(([NSString stringWithFormat:@"eof2: %lld %lld %d", audioPacketsReadCount, audioPacketsPlayedCount, numberOfBuffersUsed])); - - if (numberOfBuffersUsed == 0 && currentlyReadingEntry == currentlyPlayingEntry) - { - seekToTimeWasRequested = NO; - - if (audioQueue) - { - if ([self audioQueueIsRunning]) - { - self.internalState = AudioPlayerInternalStateFlushingAndStoppingButStillPlaying; - - LOGINFO(@"Stopping AudioQueue asynchronously"); - - if (AudioQueueStop(audioQueue, NO) != 0) - { - LOGINFO(@"Stopping AudioQueue asynchronously failed"); - - [self processFinishedPlayingViaAudioQueueStop]; - } - } - else - { - LOGINFO(@"AudioQueue already stopped"); - - [self processFinishedPlayingViaAudioQueueStop]; - } - } - } - - OSSpinLockLock(¤tEntryReferencesLock); - currentlyReadingEntry = nil; - OSSpinLockUnlock(¤tEntryReferencesLock); - } - else - { - stopReason = AudioPlayerStopReasonEof; - self.internalState = AudioPlayerInternalStateStopped; - } - - pthread_mutex_unlock(&playerMutex); -} - --(void) pause -{ - pthread_mutex_lock(&playerMutex); - { - OSStatus error; - - if (self.internalState != AudioPlayerInternalStatePaused && (self.internalState & AudioPlayerInternalStateRunning)) - { - self.stateBeforePaused = self.internalState; - self.internalState = AudioPlayerInternalStatePaused; - - if (audioQueue) - { - error = AudioQueuePause(audioQueue); - - if (error) - { - [self didEncounterError:AudioPlayerErrorQueuePauseFailed]; - - pthread_mutex_unlock(&playerMutex); - - return; - } - } - - [self wakeupPlaybackThread]; - } - } - pthread_mutex_unlock(&playerMutex); -} - --(void) resume -{ - pthread_mutex_lock(&playerMutex); - { - OSStatus error; - - if (self.internalState == AudioPlayerInternalStatePaused) - { - self.internalState = self.stateBeforePaused; - - if (seekToTimeWasRequested) - { - [self resetAudioQueueWithReason:@"from resume"]; - } - - if (audioQueue != nil) - { - if(!((self.internalState == AudioPlayerInternalStateWaitingForData) || (self.internalState == AudioPlayerInternalStateRebuffering) || (self.internalState == AudioPlayerInternalStateWaitingForDataAfterSeek)) - || [self readyToEndRebufferingState] - || [self readyToEndWaitingForDataState]) - { - error = AudioQueueStart(audioQueue, 0); - - if (error) - { - [self didEncounterError:AudioPlayerErrorQueueStartFailed]; - - pthread_mutex_unlock(&playerMutex); - - return; - } - } - } - - [self wakeupPlaybackThread]; - } - } - pthread_mutex_unlock(&playerMutex); -} - --(void) stop -{ - pthread_mutex_lock(&playerMutex); - { - if (self.internalState == AudioPlayerInternalStateStopped) - { - pthread_mutex_unlock(&playerMutex); - - return; - } - - stopReason = AudioPlayerStopReasonUserAction; - self.internalState = AudioPlayerInternalStateStopped; - - [self wakeupPlaybackThread]; - } - pthread_mutex_unlock(&playerMutex); -} - --(void) flushStop -{ - pthread_mutex_lock(&playerMutex); - { - if (self.internalState == AudioPlayerInternalStateStopped) - { - pthread_mutex_unlock(&playerMutex); - - return; - } - - stopReason = AudioPlayerStopReasonUserActionFlushStop; - self.internalState = AudioPlayerInternalStateStopped; - - [self wakeupPlaybackThread]; - } - pthread_mutex_unlock(&playerMutex); -} - --(void) stopThread -{ - BOOL wait = NO; - - NSRunLoop* runLoop = playbackThreadRunLoop; - - if (runLoop != nil) - { - wait = YES; - - [self invokeOnPlaybackThread:^ - { - pthread_mutex_lock(&playerMutex); - disposeWasRequested = YES; - pthread_mutex_unlock(&queueBuffersMutex); - }]; - - pthread_mutex_lock(&queueBuffersMutex); - pthread_cond_signal(&queueBufferReadyCondition); - pthread_mutex_unlock(&queueBuffersMutex); - - pthread_mutex_lock(&mainThreadSyncCallMutex); - pthread_cond_signal(&mainThreadSyncCallReadyCondition); - pthread_mutex_unlock(&mainThreadSyncCallMutex); - - - CFRunLoopStop([runLoop getCFRunLoop]); - } - - if (wait) - { - [threadFinishedCondLock lockWhenCondition:1]; - [threadFinishedCondLock unlockWithCondition:0]; - } -} - --(void) mute -{ - AudioQueueSetParameter(audioQueue, kAudioQueueParam_Volume, 0); -} - --(void) unmute -{ - AudioQueueSetParameter(audioQueue, kAudioQueueParam_Volume, 1); -} - --(void) dispose -{ - [self stop]; - [self stopThread]; -} - --(NSObject*) currentlyPlayingQueueItemId -{ - OSSpinLockLock(¤tEntryReferencesLock); - - STKQueueEntry* entry = currentlyPlayingEntry; - - if (entry == nil) - { - OSSpinLockUnlock(¤tEntryReferencesLock); - - return nil; - } - - NSObject* retval = entry.queueItemId; - - OSSpinLockUnlock(¤tEntryReferencesLock); - - return retval; -} - -#pragma mark Metering - --(void) setMeteringEnabled:(BOOL)value -{ - if (!audioQueue) - { - meteringEnabled = value; - - return; - } - - UInt32 on = value ? 1 : 0; - OSStatus error = AudioQueueSetProperty(audioQueue, kAudioQueueProperty_EnableLevelMetering, &on, sizeof(on)); - - if (error) - { - meteringEnabled = NO; - } - else - { - meteringEnabled = YES; - } -} - --(BOOL) meteringEnabled -{ - return meteringEnabled; -} - --(void) updateMeters -{ - if (!meteringEnabled) - { - NSAssert(NO, @"Metering is not enabled. Make sure to set meteringEnabled = YES."); - } - - NSInteger channels = currentAudioStreamBasicDescription.mChannelsPerFrame; - - if (numberOfChannels != channels) - { - numberOfChannels = channels; - - if (levelMeterState) free(levelMeterState); - { - levelMeterState = malloc(sizeof(AudioQueueLevelMeterState) * numberOfChannels); - } - } - - UInt32 sizeofMeters = (UInt32)(sizeof(AudioQueueLevelMeterState) * numberOfChannels); - - AudioQueueGetProperty(audioQueue, kAudioQueueProperty_CurrentLevelMeterDB, levelMeterState, &sizeofMeters); -} - --(float) peakPowerInDecibelsForChannel:(NSUInteger)channelNumber -{ - if (!meteringEnabled || !levelMeterState || (channelNumber > numberOfChannels)) - { - return 0; - } - - return levelMeterState[channelNumber].mPeakPower; -} - --(float) averagePowerInDecibelsForChannel:(NSUInteger)channelNumber -{ - if (!meteringEnabled || !levelMeterState || (channelNumber > numberOfChannels)) - { - return 0; - } - - return levelMeterState[channelNumber].mAveragePower; -} - -@end