diff --git a/ExampleApp/ExampleApp/AppDelegate.m b/ExampleApp/ExampleApp/AppDelegate.m index 2cc4c01..4fe456a 100644 --- a/ExampleApp/ExampleApp/AppDelegate.m +++ b/ExampleApp/ExampleApp/AppDelegate.m @@ -9,7 +9,7 @@ #import "AppDelegate.h" #import "STKAudioPlayer.h" #import "AudioPlayerView.h" -#import "STKAutoRecoveringHttpDataSource.h" +#import "STKAutoRecoveringHTTPDataSource.h" #import "SampleQueueId.h" #import @@ -49,7 +49,7 @@ { NSURL* url = [NSURL URLWithString:@"http://fs.bloom.fm/oss/audiosamples/sample.mp3"]; - STKAutoRecoveringHttpDataSource* dataSource = [[STKAutoRecoveringHttpDataSource alloc] initWithHttpDataSource:(STKHttpDataSource*)[audioPlayer dataSourceFromURL:url]]; + STKAutoRecoveringHTTPDataSource* dataSource = [[STKAutoRecoveringHTTPDataSource alloc] initWithHTTPDataSource:(STKHTTPDataSource*)[audioPlayer dataSourceFromURL:url]]; [audioPlayer setDataSource:dataSource withQueueItemId:[[SampleQueueId alloc] initWithUrl:url andCount:0]]; } diff --git a/StreamingKit/StreamingKit.xcodeproj/project.pbxproj b/StreamingKit/StreamingKit.xcodeproj/project.pbxproj index 0d65159..5f6d53b 100644 --- a/StreamingKit/StreamingKit.xcodeproj/project.pbxproj +++ b/StreamingKit/StreamingKit.xcodeproj/project.pbxproj @@ -14,11 +14,11 @@ A1E7C4E6188D57F60010896F /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = A1E7C4E4188D57F60010896F /* InfoPlist.strings */; }; A1E7C4E8188D57F60010896F /* StreamingKitTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A1E7C4E7188D57F60010896F /* StreamingKitTests.m */; }; A1E7C4FF188D5E550010896F /* STKAudioPlayer.m in Sources */ = {isa = PBXBuildFile; fileRef = A1E7C4F2188D5E550010896F /* STKAudioPlayer.m */; }; - A1E7C500188D5E550010896F /* STKAutoRecoveringHttpDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = A1E7C4F4188D5E550010896F /* STKAutoRecoveringHttpDataSource.m */; }; + A1E7C500188D5E550010896F /* STKAutoRecoveringHTTPDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = A1E7C4F4188D5E550010896F /* STKAutoRecoveringHTTPDataSource.m */; }; A1E7C501188D5E550010896F /* STKCoreFoundationDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = A1E7C4F6188D5E550010896F /* STKCoreFoundationDataSource.m */; }; A1E7C502188D5E550010896F /* STKDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = A1E7C4F8188D5E550010896F /* STKDataSource.m */; }; A1E7C503188D5E550010896F /* STKDataSourceWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = A1E7C4FA188D5E550010896F /* STKDataSourceWrapper.m */; }; - A1E7C504188D5E550010896F /* STKHttpDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = A1E7C4FC188D5E550010896F /* STKHttpDataSource.m */; }; + A1E7C504188D5E550010896F /* STKHTTPDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = A1E7C4FC188D5E550010896F /* STKHTTPDataSource.m */; }; A1E7C505188D5E550010896F /* STKLocalFileDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = A1E7C4FE188D5E550010896F /* STKLocalFileDataSource.m */; }; A1E7C508188D62D20010896F /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1E7C507188D62D20010896F /* UIKit.framework */; }; /* End PBXBuildFile section */ @@ -56,16 +56,16 @@ A1E7C4E7188D57F60010896F /* StreamingKitTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = StreamingKitTests.m; sourceTree = ""; }; A1E7C4F1188D5E550010896F /* STKAudioPlayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STKAudioPlayer.h; sourceTree = ""; }; A1E7C4F2188D5E550010896F /* STKAudioPlayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STKAudioPlayer.m; sourceTree = ""; }; - A1E7C4F3188D5E550010896F /* STKAutoRecoveringHttpDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STKAutoRecoveringHttpDataSource.h; sourceTree = ""; }; - A1E7C4F4188D5E550010896F /* STKAutoRecoveringHttpDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STKAutoRecoveringHttpDataSource.m; sourceTree = ""; }; + A1E7C4F3188D5E550010896F /* STKAutoRecoveringHTTPDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STKAutoRecoveringHTTPDataSource.h; sourceTree = ""; }; + A1E7C4F4188D5E550010896F /* STKAutoRecoveringHTTPDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STKAutoRecoveringHTTPDataSource.m; sourceTree = ""; }; A1E7C4F5188D5E550010896F /* STKCoreFoundationDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STKCoreFoundationDataSource.h; sourceTree = ""; }; A1E7C4F6188D5E550010896F /* STKCoreFoundationDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STKCoreFoundationDataSource.m; sourceTree = ""; }; A1E7C4F7188D5E550010896F /* STKDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STKDataSource.h; sourceTree = ""; }; A1E7C4F8188D5E550010896F /* STKDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STKDataSource.m; sourceTree = ""; }; A1E7C4F9188D5E550010896F /* STKDataSourceWrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STKDataSourceWrapper.h; sourceTree = ""; }; A1E7C4FA188D5E550010896F /* STKDataSourceWrapper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STKDataSourceWrapper.m; sourceTree = ""; }; - A1E7C4FB188D5E550010896F /* STKHttpDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STKHttpDataSource.h; sourceTree = ""; }; - A1E7C4FC188D5E550010896F /* STKHttpDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STKHttpDataSource.m; sourceTree = ""; }; + A1E7C4FB188D5E550010896F /* STKHTTPDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STKHTTPDataSource.h; sourceTree = ""; }; + A1E7C4FC188D5E550010896F /* STKHTTPDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STKHTTPDataSource.m; sourceTree = ""; }; A1E7C4FD188D5E550010896F /* STKLocalFileDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STKLocalFileDataSource.h; sourceTree = ""; }; A1E7C4FE188D5E550010896F /* STKLocalFileDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STKLocalFileDataSource.m; sourceTree = ""; }; A1E7C507188D62D20010896F /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; @@ -128,16 +128,16 @@ children = ( A1E7C4F1188D5E550010896F /* STKAudioPlayer.h */, A1E7C4F2188D5E550010896F /* STKAudioPlayer.m */, - A1E7C4F3188D5E550010896F /* STKAutoRecoveringHttpDataSource.h */, - A1E7C4F4188D5E550010896F /* STKAutoRecoveringHttpDataSource.m */, + A1E7C4F3188D5E550010896F /* STKAutoRecoveringHTTPDataSource.h */, + A1E7C4F4188D5E550010896F /* STKAutoRecoveringHTTPDataSource.m */, A1E7C4F5188D5E550010896F /* STKCoreFoundationDataSource.h */, A1E7C4F6188D5E550010896F /* STKCoreFoundationDataSource.m */, A1E7C4F7188D5E550010896F /* STKDataSource.h */, A1E7C4F8188D5E550010896F /* STKDataSource.m */, A1E7C4F9188D5E550010896F /* STKDataSourceWrapper.h */, A1E7C4FA188D5E550010896F /* STKDataSourceWrapper.m */, - A1E7C4FB188D5E550010896F /* STKHttpDataSource.h */, - A1E7C4FC188D5E550010896F /* STKHttpDataSource.m */, + A1E7C4FB188D5E550010896F /* STKHTTPDataSource.h */, + A1E7C4FC188D5E550010896F /* STKHTTPDataSource.m */, A1E7C4FD188D5E550010896F /* STKLocalFileDataSource.h */, A1E7C4FE188D5E550010896F /* STKLocalFileDataSource.m */, A1E7C4CE188D57F50010896F /* Supporting Files */, @@ -256,10 +256,10 @@ A1E7C501188D5E550010896F /* STKCoreFoundationDataSource.m in Sources */, A1E7C4FF188D5E550010896F /* STKAudioPlayer.m in Sources */, A1E7C505188D5E550010896F /* STKLocalFileDataSource.m in Sources */, - A1E7C504188D5E550010896F /* STKHttpDataSource.m in Sources */, + A1E7C504188D5E550010896F /* STKHTTPDataSource.m in Sources */, A1E7C503188D5E550010896F /* STKDataSourceWrapper.m in Sources */, A1E7C502188D5E550010896F /* STKDataSource.m in Sources */, - A1E7C500188D5E550010896F /* STKAutoRecoveringHttpDataSource.m in Sources */, + A1E7C500188D5E550010896F /* STKAutoRecoveringHTTPDataSource.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.h b/StreamingKit/StreamingKit/STKAudioPlayer.h index ac62c84..e52dab9 100644 --- a/StreamingKit/StreamingKit/STKAudioPlayer.h +++ b/StreamingKit/StreamingKit/STKAudioPlayer.h @@ -132,6 +132,7 @@ AudioPlayerErrorCode; -(void) queueDataSource:(STKDataSource*)dataSource withQueueItemId:(NSObject*)queueItemId; -(void) setDataSource:(STKDataSource*)dataSourceIn withQueueItemId:(NSObject*)queueItemId; -(void) seekToTime:(double)value; +-(void) clearQueue; -(void) pause; -(void) resume; -(void) stop; diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.m b/StreamingKit/StreamingKit/STKAudioPlayer.m index d259e7b..fd2633e 100644 --- a/StreamingKit/StreamingKit/STKAudioPlayer.m +++ b/StreamingKit/StreamingKit/STKAudioPlayer.m @@ -37,7 +37,7 @@ #import "STKAudioPlayer.h" #import "AudioToolbox/AudioToolbox.h" -#import "STKHttpDataSource.h" +#import "STKHTTPDataSource.h" #import "STKLocalFileDataSource.h" #import "libkern/OSAtomic.h" @@ -445,9 +445,9 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ self.state = newState; dispatch_async(dispatch_get_main_queue(), ^ - { - [self.delegate audioPlayer:self stateChanged:self.state]; - }); + { + [self.delegate audioPlayer:self stateChanged:self.state]; + }); } } @@ -599,7 +599,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ } else { - retval = [[STKHttpDataSource alloc] initWithURL:url]; + retval = [[STKHTTPDataSource alloc] initWithURL:url]; } return retval; @@ -635,13 +635,16 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ [upcomingQueue removeAllObjects]; - dispatch_async(dispatch_get_main_queue(), ^ + if (array.count > 0) { - if ([self.delegate respondsToSelector:@selector(audioPlayer:didCancelQueuedItems:)]) + dispatch_async(dispatch_get_main_queue(), ^ { - [self.delegate audioPlayer:self didCancelQueuedItems:array]; - } - }); + if ([self.delegate respondsToSelector:@selector(audioPlayer:didCancelQueuedItems:)]) + { + [self.delegate audioPlayer:self didCancelQueuedItems:array]; + } + }); + } } pthread_mutex_unlock(&playerMutex); } @@ -1209,24 +1212,24 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ if (runLoop) { CFRunLoopPerformBlock([runLoop getCFRunLoop], NSDefaultRunLoopMode, ^ - { - pthread_mutex_lock(&playerMutex); - - if (audioQueue) - { - [self stopAudioQueueWithReason:@"handlePropertyChangeForQueue`2"]; - } - - if (currentlyPlayingEntry) - { - self->stopReason = AudioPlayerStopReasonEof; - self.internalState = AudioPlayerInternalStateStopped; - - [self processFinishedPlaying:currentlyPlayingEntry]; - } - - pthread_mutex_unlock(&playerMutex); - }); + { + pthread_mutex_lock(&playerMutex); + + if (audioQueue) + { + [self stopAudioQueueWithReason:@"handlePropertyChangeForQueue`2"]; + } + + if (currentlyPlayingEntry) + { + self->stopReason = AudioPlayerStopReasonEof; + self.internalState = AudioPlayerInternalStateStopped; + + [self processFinishedPlaying:currentlyPlayingEntry]; + } + + pthread_mutex_unlock(&playerMutex); + }); CFRunLoopWakeUp([runLoop getCFRunLoop]); } @@ -1371,6 +1374,13 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ audioQueue = nil; } + if (currentlyPlayingEntry == nil) + { + pthread_mutex_unlock(&playerMutex); + + return; + } + currentAudioStreamBasicDescription = currentlyPlayingEntry->audioStreamBasicDescription; error = AudioQueueNewOutput(¤tAudioStreamBasicDescription, AudioQueueOutputCallbackProc, (__bridge void*)self, NULL, NULL, 0, &audioQueue); @@ -1381,9 +1391,9 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ { [self.delegate audioPlayer:self didEncounterError:AudioPlayerErrorQueueCreationFailed]; }); - - pthread_mutex_unlock(&playerMutex); + pthread_mutex_unlock(&playerMutex); + return; } @@ -1397,7 +1407,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ }); pthread_mutex_unlock(&playerMutex); - + return; } @@ -1497,7 +1507,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ { [self.delegate audioPlayer:self didEncounterError:AudioPlayerErrorQueueCreationFailed]; }); - + pthread_mutex_unlock(&playerMutex); return; @@ -1755,7 +1765,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ { dispatch_async(dispatch_get_main_queue(), ^ { - [self.delegate audioPlayer:self didStartPlayingQueueItemId:playingQueueItemId]; + [self.delegate audioPlayer:self didStartPlayingQueueItemId:playingQueueItemId]; }); } } @@ -2082,6 +2092,8 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ currentEntry.bytesBuffered = 0; currentEntry.firstFrameIndex = [self currentTimeInFrames]; + + [self clearQueue]; } -(BOOL) startAudioQueue @@ -2337,15 +2349,37 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ [self logInfo:[NSString stringWithFormat:@"dataSourceEofLastByteIndex: %lld, %lld", audioPacketsReadCount, audioPacketsPlayedCount]]; - if (audioPacketsReadCount == 0 && audioPacketsPlayedCount == audioPacketsReadCount) + if (audioPacketsReadCount == 0 && audioPacketsPlayedCount == audioPacketsReadCount && currentlyReadingEntry == currentlyPlayingEntry) { - [self logInfo:@"dataSourceEof shutting down audio queue"]; + [self logInfo:@"dataSourceEof seeked to end"]; if (audioQueue) { self.internalState = AudioPlayerInternalStateFlushingAndStoppingButStillPlaying; - AudioQueueStop(audioQueue, NO); + if ([self audioQueueIsRunning]) + { + [self logInfo:@"dataSourceEof stopping audio queue asynchronously"]; + + AudioQueueStop(audioQueue, NO); + } + else + { + [self logInfo:@"dataSourceEof finished playing"]; + + if (audioQueue) + { + [self stopAudioQueueWithReason:@"dataSourceEof finished playing"]; + } + + if (currentlyPlayingEntry) + { + self->stopReason = AudioPlayerStopReasonEof; + self.internalState = AudioPlayerInternalStateStopped; + + [self processFinishedPlaying:currentlyPlayingEntry]; + } + } } } diff --git a/StreamingKit/StreamingKit/STKAutoRecoveringHttpDataSource.h b/StreamingKit/StreamingKit/STKAutoRecoveringHTTPDataSource.h similarity index 90% rename from StreamingKit/StreamingKit/STKAutoRecoveringHttpDataSource.h rename to StreamingKit/StreamingKit/STKAutoRecoveringHTTPDataSource.h index fdb3b69..e08f767 100644 --- a/StreamingKit/StreamingKit/STKAutoRecoveringHttpDataSource.h +++ b/StreamingKit/StreamingKit/STKAutoRecoveringHTTPDataSource.h @@ -33,13 +33,13 @@ **********************************************************************************/ #import "STKDataSource.h" -#import "STKHttpDataSource.h" +#import "STKHTTPDataSource.h" #import "STKDataSourceWrapper.h" -@interface STKAutoRecoveringHttpDataSource : STKDataSourceWrapper +@interface STKAutoRecoveringHTTPDataSource : STKDataSourceWrapper --(id) initWithHttpDataSource:(STKHttpDataSource*)innerDataSource; +-(id) initWithHTTPDataSource:(STKHTTPDataSource*)innerDataSource; -@property (readonly) STKHttpDataSource* innerDataSource; +@property (readonly) STKHTTPDataSource* innerDataSource; @end diff --git a/StreamingKit/StreamingKit/STKAutoRecoveringHttpDataSource.m b/StreamingKit/StreamingKit/STKAutoRecoveringHTTPDataSource.m similarity index 92% rename from StreamingKit/StreamingKit/STKAutoRecoveringHttpDataSource.m rename to StreamingKit/StreamingKit/STKAutoRecoveringHTTPDataSource.m index 2a31d1b..af544b7 100644 --- a/StreamingKit/StreamingKit/STKAutoRecoveringHttpDataSource.m +++ b/StreamingKit/StreamingKit/STKAutoRecoveringHTTPDataSource.m @@ -40,12 +40,12 @@ #import #import #import -#import "STKAutoRecoveringHttpDataSource.h" +#import "STKAutoRecoveringHTTPDataSource.h" #define MAX_IMMEDIATE_RECONNECT_ATTEMPTS (8) #define MAX_ATTEMPTS_WITH_SERVER_ERROR (MAX_IMMEDIATE_RECONNECT_ATTEMPTS + 2) -@interface STKAutoRecoveringHttpDataSource() +@interface STKAutoRecoveringHTTPDataSource() { int reconnectAttempts; BOOL waitingForNetwork; @@ -60,25 +60,25 @@ static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReach { @autoreleasepool { - STKAutoRecoveringHttpDataSource* dataSource = (__bridge STKAutoRecoveringHttpDataSource*)info; + STKAutoRecoveringHTTPDataSource* dataSource = (__bridge STKAutoRecoveringHTTPDataSource*)info; [dataSource reachabilityChanged]; } } -@implementation STKAutoRecoveringHttpDataSource +@implementation STKAutoRecoveringHTTPDataSource --(STKHttpDataSource*) innerHttpDataSource +-(STKHTTPDataSource*) innerHTTPDataSource { - return (STKHttpDataSource*)self.innerDataSource; + return (STKHTTPDataSource*)self.innerDataSource; } -(id) initWithDataSource:(STKDataSource *)innerDataSource { - return [self initWithHttpDataSource:(STKHttpDataSource*)innerDataSource]; + return [self initWithHTTPDataSource:(STKHTTPDataSource*)innerDataSource]; } --(id) initWithHttpDataSource:(STKHttpDataSource*)innerDataSourceIn +-(id) initWithHTTPDataSource:(STKHTTPDataSource*)innerDataSourceIn { if (self = [super initWithDataSource:innerDataSourceIn]) { @@ -148,7 +148,7 @@ static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReach -(void) dealloc { - NSLog(@"STKAutoRecoveringHttpDataSource dealloc"); + NSLog(@"STKAutoRecoveringHTTPDataSource dealloc"); self.innerDataSource.delegate = nil; diff --git a/StreamingKit/StreamingKit/STKCoreFoundationDataSource.h b/StreamingKit/StreamingKit/STKCoreFoundationDataSource.h index 85798ea..fa1a1b6 100644 --- a/StreamingKit/StreamingKit/STKCoreFoundationDataSource.h +++ b/StreamingKit/StreamingKit/STKCoreFoundationDataSource.h @@ -44,10 +44,13 @@ @interface STKCoreFoundationDataSource : STKDataSource { @protected + BOOL isInErrorState; CFReadStreamRef stream; NSRunLoop* eventsRunLoop; } +@property (readonly) BOOL isInErrorState; + -(BOOL) reregisterForEvents; -(void) dataAvailable; diff --git a/StreamingKit/StreamingKit/STKCoreFoundationDataSource.m b/StreamingKit/StreamingKit/STKCoreFoundationDataSource.m index 13622db..423243e 100644 --- a/StreamingKit/StreamingKit/STKCoreFoundationDataSource.m +++ b/StreamingKit/StreamingKit/STKCoreFoundationDataSource.m @@ -60,6 +60,11 @@ static void ReadStreamCallbackProc(CFReadStreamRef stream, CFStreamEventType eve @implementation STKCoreFoundationDataSource +-(BOOL) isInErrorState +{ + return self->isInErrorState; +} + -(void) dataAvailable { [self.delegate dataSourceDataAvailable:self]; @@ -72,6 +77,8 @@ static void ReadStreamCallbackProc(CFReadStreamRef stream, CFStreamEventType eve -(void) errorOccured { + self->isInErrorState = YES; + [self.delegate dataSourceErrorOccured:self]; } diff --git a/StreamingKit/StreamingKit/STKHttpDataSource.h b/StreamingKit/StreamingKit/STKHTTPDataSource.h similarity index 84% rename from StreamingKit/StreamingKit/STKHttpDataSource.h rename to StreamingKit/StreamingKit/STKHTTPDataSource.h index 2264cf9..4057bd7 100644 --- a/StreamingKit/StreamingKit/STKHttpDataSource.h +++ b/StreamingKit/StreamingKit/STKHTTPDataSource.h @@ -34,15 +34,20 @@ #import "STKCoreFoundationDataSource.h" -typedef NSURL*(^URLProvider)(); +@class STKHTTPDataSource; -@interface STKHttpDataSource : STKCoreFoundationDataSource +typedef void(^STKURLBlock)(NSURL* url); +typedef NSURL*(^STKURLProvider)(); +typedef void(^STKAsyncURLProvider)(STKHTTPDataSource* dataSource, BOOL forSeek, STKURLBlock callback); + +@interface STKHTTPDataSource : STKCoreFoundationDataSource @property (readonly, retain) NSURL* url; @property (readwrite) UInt32 httpStatusCode; +(AudioFileTypeID) audioFileTypeHintFromMimeType:(NSString*)fileExtension; -(id) initWithURL:(NSURL*)url; --(id) initWithURLProvider:(URLProvider)urlProvider; +-(id) initWithURLProvider:(STKURLProvider)urlProvider; +-(id) initWithAsyncURLProvider:(STKAsyncURLProvider)asyncUrlProvider; @end diff --git a/StreamingKit/StreamingKit/STKHttpDataSource.m b/StreamingKit/StreamingKit/STKHTTPDataSource.m similarity index 66% rename from StreamingKit/StreamingKit/STKHttpDataSource.m rename to StreamingKit/StreamingKit/STKHTTPDataSource.m index d4fd29d..a67e7dc 100644 --- a/StreamingKit/StreamingKit/STKHttpDataSource.m +++ b/StreamingKit/StreamingKit/STKHTTPDataSource.m @@ -32,10 +32,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **********************************************************************************/ -#import "STKHttpDataSource.h" +#import "STKHTTPDataSource.h" #import "STKLocalFileDataSource.h" -@interface STKHttpDataSource() +@interface STKHTTPDataSource() { @private int seekStart; @@ -43,7 +43,7 @@ long long fileLength; int discontinuous; NSURL* currentUrl; - URLProvider urlProvider; + STKAsyncURLProvider asyncUrlProvider; NSDictionary* httpHeaders; AudioFileTypeID audioFileTypeHint; } @@ -51,14 +51,24 @@ @end -@implementation STKHttpDataSource +@implementation STKHTTPDataSource -(id) initWithURL:(NSURL*)urlIn { return [self initWithURLProvider:^NSURL* { return urlIn; }]; } --(id) initWithURLProvider:(URLProvider)urlProviderIn +-(id) initWithURLProvider:(STKURLProvider)urlProviderIn +{ + urlProviderIn = [urlProviderIn copy]; + + return [self initWithAsyncURLProvider:^(STKHTTPDataSource* dataSource, BOOL forSeek, STKURLBlock block) + { + block(urlProviderIn()); + }]; +} + +-(id) initWithAsyncURLProvider:(STKAsyncURLProvider)asyncUrlProviderIn { if (self = [super init]) { @@ -66,7 +76,7 @@ relativePosition = 0; fileLength = -1; - self->urlProvider = [urlProviderIn copy]; + self->asyncUrlProvider = [asyncUrlProviderIn copy]; [self open]; @@ -78,7 +88,7 @@ -(void) dealloc { - NSLog(@"STKHttpDataSource dealloc"); + NSLog(@"STKHTTPDataSource dealloc"); } -(NSURL*) url @@ -148,7 +158,7 @@ } NSString* contentType = [httpHeaders objectForKey:@"Content-Type"]; - AudioFileTypeID typeIdFromMimeType = [STKHttpDataSource audioFileTypeHintFromMimeType:contentType]; + AudioFileTypeID typeIdFromMimeType = [STKHTTPDataSource audioFileTypeHintFromMimeType:contentType]; if (typeIdFromMimeType != 0) { @@ -193,8 +203,9 @@ relativePosition = 0; seekStart = (int)offset; - [self open]; - [self reregisterForEvents]; + self->isInErrorState = NO; + + [self openForSeek:YES]; } -(int) readIntoBuffer:(UInt8*)buffer withSize:(int)size @@ -218,75 +229,95 @@ -(void) open { - self->currentUrl = urlProvider(); - - CFHTTPMessageRef message = CFHTTPMessageCreateRequest(NULL, (CFStringRef)@"GET", (__bridge CFURLRef)self->currentUrl, kCFHTTPVersion1_1); - - if (seekStart > 0) - { - CFHTTPMessageSetHeaderFieldValue(message, CFSTR("Range"), (__bridge CFStringRef)[NSString stringWithFormat:@"bytes=%d-", seekStart]); - - discontinuous = YES; - } - - stream = CFReadStreamCreateForHTTPRequest(NULL, message); - - if (stream == nil) - { - CFRelease(message); - - [self errorOccured]; - - return; - } - - if (!CFReadStreamSetProperty(stream, kCFStreamPropertyHTTPShouldAutoredirect, kCFBooleanTrue)) - { - CFRelease(message); - - [self errorOccured]; - - return; - } - - // Proxy support - - CFDictionaryRef proxySettings = CFNetworkCopySystemProxySettings(); - CFReadStreamSetProperty(stream, kCFStreamPropertyHTTPProxy, proxySettings); - CFRelease(proxySettings); - - // SSL support - - if ([self->currentUrl.scheme caseInsensitiveCompare:@"https"] == NSOrderedSame) - { - NSDictionary* sslSettings = [NSDictionary dictionaryWithObjectsAndKeys: - (NSString*)kCFStreamSocketSecurityLevelNegotiatedSSL, kCFStreamSSLLevel, - [NSNumber numberWithBool:YES], kCFStreamSSLAllowsExpiredCertificates, - [NSNumber numberWithBool:YES], kCFStreamSSLAllowsExpiredRoots, - [NSNumber numberWithBool:YES], kCFStreamSSLAllowsAnyRoot, - [NSNumber numberWithBool:NO], kCFStreamSSLValidatesCertificateChain, - [NSNull null], kCFStreamSSLPeerName, - nil]; - - CFReadStreamSetProperty(stream, kCFStreamPropertySSLSettings, (__bridge CFTypeRef)sslSettings); - } - - // Open - - if (!CFReadStreamOpen(stream)) - { - CFRelease(stream); - CFRelease(message); - - [self errorOccured]; - - return; - } - - CFRelease(message); + return [self openForSeek:NO]; } -- (NSString *)description +-(void) openForSeek:(BOOL)forSeek +{ + asyncUrlProvider(self, forSeek, ^(NSURL* url) + { + self->currentUrl = url; + + if (url == nil) + { + return; + } + + CFHTTPMessageRef message = CFHTTPMessageCreateRequest(NULL, (CFStringRef)@"GET", (__bridge CFURLRef)self->currentUrl, kCFHTTPVersion1_1); + + if (seekStart > 0) + { + CFHTTPMessageSetHeaderFieldValue(message, CFSTR("Range"), (__bridge CFStringRef)[NSString stringWithFormat:@"bytes=%d-", seekStart]); + + discontinuous = YES; + } + + stream = CFReadStreamCreateForHTTPRequest(NULL, message); + + if (stream == nil) + { + CFRelease(message); + + [self errorOccured]; + + return; + } + + if (!CFReadStreamSetProperty(stream, kCFStreamPropertyHTTPShouldAutoredirect, kCFBooleanTrue)) + { + CFRelease(message); + + [self errorOccured]; + + return; + } + + // Proxy support + + CFDictionaryRef proxySettings = CFNetworkCopySystemProxySettings(); + CFReadStreamSetProperty(stream, kCFStreamPropertyHTTPProxy, proxySettings); + CFRelease(proxySettings); + + // SSL support + + if ([self->currentUrl.scheme caseInsensitiveCompare:@"https"] == NSOrderedSame) + { + NSDictionary* sslSettings = [NSDictionary dictionaryWithObjectsAndKeys: + (NSString*)kCFStreamSocketSecurityLevelNegotiatedSSL, kCFStreamSSLLevel, + [NSNumber numberWithBool:YES], kCFStreamSSLAllowsExpiredCertificates, + [NSNumber numberWithBool:YES], kCFStreamSSLAllowsExpiredRoots, + [NSNumber numberWithBool:YES], kCFStreamSSLAllowsAnyRoot, + [NSNumber numberWithBool:NO], kCFStreamSSLValidatesCertificateChain, + [NSNull null], kCFStreamSSLPeerName, + nil]; + + CFReadStreamSetProperty(stream, kCFStreamPropertySSLSettings, (__bridge CFTypeRef)sslSettings); + } + + // Open + + if (!CFReadStreamOpen(stream)) + { + CFRelease(stream); + CFRelease(message); + + [self errorOccured]; + + return; + } + + self->isInErrorState = NO; + + if (self->eventsRunLoop) + { + [self reregisterForEvents]; + } + + CFRelease(message); + }); +} + +-(NSString*) description { return [NSString stringWithFormat:@"HTTP data source with file length: %lld and position: %lld", self.length, self.position]; }