Compare commits

...

39 Commits

Author SHA1 Message Date
Thong Nguyen 8dd611c2ac Prefixed all enum names and values 2014-01-28 14:04:58 +00:00
Thong Nguyen 4a0f944550 Removed some logging 2014-01-28 13:55:00 +00:00
Thong Nguyen f60e16c804 Tidied up some field types. Changed duration to return progress if progress is larger than duration and there is a duration 2014-01-27 21:33:10 +00:00
Thong Nguyen f3f52cf1cd Allow progress to be returned if the duration is 0 2014-01-27 19:46:35 +00:00
Thong Nguyen 53667f8a35 New lower-latecy rebuffering support 2014-01-27 19:38:32 +00:00
Thong Nguyen 6beebca443 Fixed deadlock on stopThread and dataSourceEof not always setting currentlReadingEntry to nil 2014-01-27 16:28:38 +00:00
Thong Nguyen 6af12221e5 Fixed potential crash in handleAudioPackets 2014-01-27 16:22:51 +00:00
Thong Nguyen ca80243083 Optimised seekToTime to avoid using the player thread mutex to allow smoother scrubbing 2014-01-27 02:00:01 +00:00
Thong Nguyen 804aa9dd6e All stopAudioQueue calls are now directed via the playback thread to reduce the need to have locks inside handleAudioQueueOutput. Added autorelease pool around regular processRunLoop call to fix STKDataSources not being freed (amongst other things). Fixed sometimes seeking to the very end of a slow loading track causes the didFinishPlayingQueueItemId event to not be raised 2014-01-27 01:23:18 +00:00
Thong Nguyen 0fdb461d90 Fixed bug in last commit that cuased playback to pause when seeking a file that's already fully buffered 2014-01-27 00:26:36 +00:00
Thong Nguyen 313a55a5ca Added more stable duration calculations 2014-01-26 23:57:00 +00:00
Thong Nguyen ba8b29d106 Fixed progress not correct when skipping a new track and the new track takes a while to buffer 2014-01-26 22:56:24 +00:00
Thong Nguyen 5b629665d6 createAudioQueue creates queues that start off paused by defualt to keep correct time if buffering is required befor the first buffers are rendered 2014-01-26 22:09:21 +00:00
Thong Nguyen 7608a42d29 Fixed dispose call hanging if player or audio thread are doing sync calls to the UI thread. Better rebuffering on 2G 2014-01-26 20:37:06 +00:00
Thong Nguyen 381795fed6 Fixed potential deadlock with clearQueue invokes callback on playback thread that may be blocking/waiting on audio packets 2014-01-26 15:43:39 +00:00
Thong Nguyen bd1d3c9aea A lot more support and fixups for rebuffering including new AudioPlayerStateBuffering state 2014-01-26 15:30:16 +00:00
Thong Nguyen 6ada345b17 Added better rebuffering handling. Pauses while rebuffering and doesn't start until there's a decent number of frames available 2014-01-26 01:04:24 +00:00
Thong Nguyen e495130219 More STKAutoRecoveringHTTPDataSource improvements 2014-01-25 14:30:41 +00:00
Thong Nguyen e843a0b730 Refactor and optimisation of STKHTTPDataSource and STKAutoRecoveringHTTPDataSource 2014-01-25 14:11:32 +00:00
Thong Nguyen b7f87aea98 Fixed next file won't start playing if queued on just before current one finishes 2014-01-25 13:59:11 +00:00
Thong Nguyen ca7abd75c1 Changed DataSources to open on-demand and also for callback registration to be performed before opening 2014-01-25 13:27:25 +00:00
Thong Nguyen 951f905516 Quite a big refactor to fix simplify and fix numerous threading and playback/position/buffering issues. Also all events are now synchronously raised to the UI thread via the playback thread which helps prevent lagged queued operations amongst other things 2014-01-24 19:11:52 +00:00
Thong Nguyen bd8e6a5da9 Fixes for errors when user skips to near the end of a track and another track is queued 2014-01-23 19:15:32 +00:00
Thong Nguyen 48ce1f9e67 Renamed all Http classes to HTTP. Udpated STKHTTPDataSource to accept asynchronous URL providers. Chnaged STKAudioPlaye to always flush upcoming queue when currently playing track is seeked 2014-01-23 16:23:18 +00:00
Thong Nguyen 257532c6c9 Added progress text to example app. Fixed finishedEvent not always being raised when user seeks to the last byte of the track 2014-01-23 12:43:22 +00:00
Thong Nguyen f85464236a Fix potential race/crash in createAudioQueue 2014-01-23 11:47:47 +00:00
Thong Nguyen b47771ae48 Formatting tidy-ups 2014-01-22 23:36:59 +00:00
Thong Nguyen 48e082ab88 Better default buffer sizes 2014-01-22 23:34:45 +00:00
Thong Nguyen b68e813750 Better default buffer sizes. Some code formatting tidy-ups 2014-01-22 23:33:49 +00:00
Thong Nguyen b4948882e4 Fixed async AudioQueue stop method for determing if a queued item has finished not taking into account upcoming queued items that aren't being decoded because they're incompatible 2014-01-22 22:52:51 +00:00
Thong Nguyen c100cb7202 Using a better mechanism for picking up when the last file on the queue is finished. Dont async-stop the AudioQueue unless it's nearing the end (handled in handleAudioQueueOutput rathe than dataSourceEof) 2014-01-22 22:41:24 +00:00
Thong Nguyen 593a08a04f Using a better mechanism for picking up when the last file on the queue is finished. Dont async-stop the AudioQueue unless it's nearing the end (handled in handleAudioQueueOutput rathe than dataSourceEof) 2014-01-22 22:40:23 +00:00
Thong Nguyen 1c9bd328e7 Removed test code and fixed an issue with not properly checking if the current queue should be stopped cause it's the last in the queue 2014-01-22 19:43:36 +00:00
Thong Nguyen dde738dffd Fixed finished event not raised if item is only item in queue. New timeline method of checking whether item has finished playing won't work if item is the only or last item in the queue as handleAudioQueueOutput isn't called once last packet is played (it's gets called if other items are queued). If no items are queued, the queue is stopped and the queue property event is used to figure out if its finished playing 2014-01-22 19:34:17 +00:00
Thong Nguyen a9bd3557a5 Changed progress to by more accurate by using AudioTime/timelines rather than bytes buffered 2014-01-22 16:52:47 +00:00
Thong Nguyen 431dd5d7f3 Moved AudioPlayerDefaultNumberOfAudioQueueBuffers define into source file from header 2014-01-22 10:25:30 +00:00
Thong Nguyen aaaed1f33f Added arm64 build back into StreamingKit. Moved private stuff out of STKAudioPlayer header files 2014-01-22 10:24:33 +00:00
Thong Nguyen 3563fb7ea1 Fixed STKHttpDataSource not copying urlProvider block 2014-01-22 09:38:37 +00:00
Thong Nguyen c9ba30b692 Removed iOS7 specific calls. Changed default target to iOS 4.3 2014-01-21 15:48:12 +00:00
17 changed files with 4772 additions and 785 deletions
@@ -23,6 +23,7 @@
A1115967188D6AEE00641365 /* AudioPlayerView.m in Sources */ = {isa = PBXBuildFile; fileRef = A1115966188D6AEE00641365 /* AudioPlayerView.m */; };
A111596C188D6C8100641365 /* sample.m4a in Resources */ = {isa = PBXBuildFile; fileRef = A111596B188D6C8100641365 /* sample.m4a */; };
A111596F188D6DB100641365 /* SampleQueueId.m in Sources */ = {isa = PBXBuildFile; fileRef = A111596E188D6DB100641365 /* SampleQueueId.m */; };
A142571D189079BE005F0129 /* airplane.aac in Resources */ = {isa = PBXBuildFile; fileRef = A142571C18907861005F0129 /* airplane.aac */; };
A1EBEE64188DE34500681B04 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1EBEE63188DE34500681B04 /* SystemConfiguration.framework */; };
/* End PBXBuildFile section */
@@ -59,6 +60,7 @@
A111596B188D6C8100641365 /* sample.m4a */ = {isa = PBXFileReference; lastKnownFileType = file; name = sample.m4a; path = Resources/sample.m4a; sourceTree = "<group>"; };
A111596D188D6DB100641365 /* SampleQueueId.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SampleQueueId.h; sourceTree = "<group>"; };
A111596E188D6DB100641365 /* SampleQueueId.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SampleQueueId.m; sourceTree = "<group>"; };
A142571C18907861005F0129 /* airplane.aac */ = {isa = PBXFileReference; lastKnownFileType = file; path = airplane.aac; sourceTree = "<group>"; };
A1EBEE63188DE34500681B04 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; };
/* End PBXFileReference section */
@@ -168,6 +170,7 @@
A111596A188D6C4B00641365 /* Resources */ = {
isa = PBXGroup;
children = (
A142571C18907861005F0129 /* airplane.aac */,
A111596B188D6C8100641365 /* sample.m4a */,
);
name = Resources;
@@ -251,6 +254,7 @@
A111593F188D686000641365 /* InfoPlist.strings in Resources */,
A111596C188D6C8100641365 /* sample.m4a in Resources */,
A1115947188D686000641365 /* Images.xcassets in Resources */,
A142571D189079BE005F0129 /* airplane.aac in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -401,11 +405,13 @@
A111595E188D686000641365 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "ExampleApp/ExampleApp-Prefix.pch";
INFOPLIST_FILE = "ExampleApp/ExampleApp-Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 6.0;
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = app;
};
@@ -414,11 +420,13 @@
A111595F188D686000641365 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "ExampleApp/ExampleApp-Prefix.pch";
INFOPLIST_FILE = "ExampleApp/ExampleApp-Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 6.0;
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = app;
};
+11 -4
View File
@@ -9,7 +9,7 @@
#import "AppDelegate.h"
#import "STKAudioPlayer.h"
#import "AudioPlayerView.h"
#import "STKAutoRecoveringHttpDataSource.h"
#import "STKAutoRecoveringHTTPDataSource.h"
#import "SampleQueueId.h"
#import <AVFoundation/AVFoundation.h>
@@ -49,18 +49,25 @@
{
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]];
}
-(void) audioPlayerViewQueueShortFileSelected:(AudioPlayerView*)audioPlayerView
{
NSString* path = [[NSBundle mainBundle] pathForResource:@"airplane" ofType:@"aac"];
NSURL* url = [NSURL fileURLWithPath:path];
[audioPlayer queueDataSource:[audioPlayer dataSourceFromURL:url] withQueueItemId:[[SampleQueueId alloc] initWithUrl:url andCount:0]];
}
-(void) audioPlayerViewPlayFromLocalFileSelected:(AudioPlayerView*)audioPlayerView
{
NSString * path = [[NSBundle mainBundle] pathForResource:@"sample" ofType:@"m4a"];
NSString* path = [[NSBundle mainBundle] pathForResource:@"sample" ofType:@"m4a"];
NSURL* url = [NSURL fileURLWithPath:path];
[audioPlayer setDataSource:[audioPlayer dataSourceFromURL:url] withQueueItemId:[[SampleQueueId alloc] initWithUrl:url andCount:0]];
}
@end
+6
View File
@@ -39,6 +39,7 @@
@protocol AudioPlayerViewDelegate<NSObject>
-(void) audioPlayerViewPlayFromHTTPSelected:(AudioPlayerView*)audioPlayerView;
-(void) audioPlayerViewQueueShortFileSelected:(AudioPlayerView*)audioPlayerView;
-(void) audioPlayerViewPlayFromLocalFileSelected:(AudioPlayerView*)audioPlayerView;
@end
@@ -46,9 +47,14 @@
{
@private
NSTimer* timer;
UILabel* label;
UILabel* statusLabel;
UISlider* slider;
UISwitch* repeatSwitch;
UIButton* playButton;
UIButton* disposeButton;
UIButton* playFromHTTPButton;
UIButton* queueShortFileButton;
UIButton* playFromLocalFileButton;
}
+99 -21
View File
@@ -64,20 +64,47 @@
playFromLocalFileButton.frame = CGRectMake((320 - size.width) / 2, frame.size.height * 0.15 + 50, size.width, size.height);
[playFromLocalFileButton addTarget:self action:@selector(playFromLocalFileButtonTouched) forControlEvents:UIControlEventTouchUpInside];
[playFromLocalFileButton setTitle:@"Play from Local File" forState:UIControlStateNormal];
queueShortFileButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
queueShortFileButton.frame = CGRectMake((320 - size.width) / 2, frame.size.height * 0.15 + 100, size.width, size.height);
[queueShortFileButton addTarget:self action:@selector(queueShortFileButtonTouched) forControlEvents:UIControlEventTouchUpInside];
[queueShortFileButton setTitle:@"Queue short file" forState:UIControlStateNormal];
playButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
playButton.frame = CGRectMake((320 - size.width) / 2, 350, size.width, size.height);
playButton.frame = CGRectMake(30, 380, size.width, size.height);
[playButton addTarget:self action:@selector(playButtonPressed) forControlEvents:UIControlEventTouchUpInside];
disposeButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
disposeButton.frame = CGRectMake((320 - size.width) - 30, 380, size.width, size.height);
[disposeButton addTarget:self action:@selector(disposeButtonPressed) forControlEvents:UIControlEventTouchUpInside];
[disposeButton setTitle:@"Dispose" forState:UIControlStateNormal];
slider = [[UISlider alloc] initWithFrame:CGRectMake(20, 290, 280, 20)];
slider = [[UISlider alloc] initWithFrame:CGRectMake(20, 320, 280, 20)];
slider.continuous = YES;
[slider addTarget:self action:@selector(sliderChanged) forControlEvents:UIControlEventValueChanged];
size = CGSizeMake(80, 50);
repeatSwitch = [[UISwitch alloc] initWithFrame:CGRectMake((320 - size.width) / 2, frame.size.height * 0.15 + 170, size.width, size.height)];
label = [[UILabel alloc] initWithFrame:CGRectMake(0, slider.frame.origin.y + slider.frame.size.height, frame.size.width, 50)];
label.textAlignment = NSTextAlignmentCenter;
statusLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, slider.frame.origin.y + slider.frame.size.height + label.frame.size.height + 8, frame.size.width, 50)];
statusLabel.textAlignment = NSTextAlignmentCenter;
[self addSubview:slider];
[self addSubview:playButton];
[self addSubview:playFromHTTPButton];
[self addSubview:playFromLocalFileButton];
[self addSubview:queueShortFileButton];
[self addSubview:repeatSwitch];
[self addSubview:label];
[self addSubview:statusLabel];
[self addSubview:disposeButton];
[self setupTimer];
[self updateControls];
}
@@ -99,24 +126,40 @@
-(void) setupTimer
{
timer = [NSTimer timerWithTimeInterval:0.25 target:self selector:@selector(tick) userInfo:nil repeats:YES];
timer = [NSTimer timerWithTimeInterval:0.05 target:self selector:@selector(tick) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
}
-(void) tick
{
if (!audioPlayer || audioPlayer.duration == 0)
if (!audioPlayer)
{
slider.value = 0;
label.text = @"";
statusLabel.text = @"";
return;
}
slider.minimumValue = 0;
slider.maximumValue = audioPlayer.duration;
slider.value = audioPlayer.progress;
if (audioPlayer.duration != 0)
{
slider.minimumValue = 0;
slider.maximumValue = audioPlayer.duration;
slider.value = audioPlayer.progress;
label.text = [NSString stringWithFormat:@"%@ - %@", [self formatTimeFromSeconds:audioPlayer.progress], [self formatTimeFromSeconds:audioPlayer.duration]];
}
else
{
slider.value = 0;
slider.minimumValue = 0;
slider.maximumValue = 0;
label.text = @"";
}
statusLabel.text = audioPlayer.state == STKAudioPlayerStateBuffering ? @"buffering" : @"";
}
-(void) playFromHTTPButtonTouched
@@ -129,14 +172,29 @@
[self.delegate audioPlayerViewPlayFromLocalFileSelected:self];
}
-(void) queueShortFileButtonTouched
{
[self.delegate audioPlayerViewQueueShortFileSelected:self];
}
-(void) disposeButtonPressed
{
[audioPlayer dispose];
audioPlayer = nil;
}
-(void) playButtonPressed
{
if (!audioPlayer)
{
return;
}
[audioPlayer dispose];
return;
if (audioPlayer.state == AudioPlayerStatePaused)
if (audioPlayer.state == STKAudioPlayerStatePaused)
{
[audioPlayer resume];
}
@@ -146,24 +204,36 @@
}
}
-(NSString*) formatTimeFromSeconds:(int)totalSeconds
{
int seconds = totalSeconds % 60;
int minutes = (totalSeconds / 60) % 60;
int hours = totalSeconds / 3600;
return [NSString stringWithFormat:@"%02d:%02d:%02d", hours, minutes, seconds];
}
-(void) updateControls
{
if (audioPlayer == nil)
{
[playButton setTitle:@"Play" forState:UIControlStateNormal];
[playButton setTitle:@"" forState:UIControlStateNormal];
}
else if (audioPlayer.state == AudioPlayerStatePaused)
else if (audioPlayer.state == STKAudioPlayerStatePaused)
{
[playButton setTitle:@"Resume" forState:UIControlStateNormal];
}
else if (audioPlayer.state == AudioPlayerStatePlaying)
else if (audioPlayer.state & STKAudioPlayerStatePlaying)
{
[playButton setTitle:@"Pause" forState:UIControlStateNormal];
}
else
{
[playButton setTitle:@"Play" forState:UIControlStateNormal];
[playButton setTitle:@"" forState:UIControlStateNormal];
}
[self tick];
}
-(void) setAudioPlayer:(STKAudioPlayer*)value
@@ -184,12 +254,12 @@
return audioPlayer;
}
-(void) audioPlayer:(STKAudioPlayer*)audioPlayer stateChanged:(AudioPlayerState)state
-(void) audioPlayer:(STKAudioPlayer*)audioPlayer stateChanged:(STKAudioPlayerState)state
{
[self updateControls];
}
-(void) audioPlayer:(STKAudioPlayer*)audioPlayer didEncounterError:(AudioPlayerErrorCode)errorCode
-(void) audioPlayer:(STKAudioPlayer*)audioPlayer didEncounterError:(STKAudioPlayerErrorCode)errorCode
{
[self updateControls];
}
@@ -209,14 +279,17 @@
// This queues on the currently playing track to be buffered and played immediately after (gapless)
SampleQueueId* queueId = (SampleQueueId*)queueItemId;
if (repeatSwitch.on)
{
SampleQueueId* queueId = (SampleQueueId*)queueItemId;
NSLog(@"Requeuing: %@", [queueId.url description]);
NSLog(@"Requeuing: %@", [queueId.url description]);
[self->audioPlayer queueDataSource:[self->audioPlayer dataSourceFromURL:queueId.url] withQueueItemId:[[SampleQueueId alloc] initWithUrl:queueId.url andCount:queueId.count + 1]];
[self->audioPlayer queueDataSource:[self->audioPlayer dataSourceFromURL:queueId.url] withQueueItemId:[[SampleQueueId alloc] initWithUrl:queueId.url andCount:queueId.count + 1]];
}
}
-(void) audioPlayer:(STKAudioPlayer*)audioPlayer didFinishPlayingQueueItemId:(NSObject*)queueItemId withReason:(AudioPlayerStopReason)stopReason andProgress:(double)progress andDuration:(double)duration
-(void) audioPlayer:(STKAudioPlayer*)audioPlayer didFinishPlayingQueueItemId:(NSObject*)queueItemId withReason:(STKAudioPlayerStopReason)stopReason andProgress:(double)progress andDuration:(double)duration
{
[self updateControls];
@@ -225,4 +298,9 @@
NSLog(@"Finished: %@", [queueId.url description]);
}
-(void) audioPlayer:(STKAudioPlayer *)audioPlayer logInfo:(NSString *)line
{
NSLog(@"%@", line);
}
@end
+5
View File
@@ -36,4 +36,9 @@
return [((SampleQueueId*)object).url isEqual: self.url] && ((SampleQueueId*)object).count == self.count;
}
-(NSString*) description
{
return [self.url description];
}
@end
Binary file not shown.
@@ -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 = "<group>"; };
A1E7C4F1188D5E550010896F /* STKAudioPlayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STKAudioPlayer.h; sourceTree = "<group>"; };
A1E7C4F2188D5E550010896F /* STKAudioPlayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STKAudioPlayer.m; sourceTree = "<group>"; };
A1E7C4F3188D5E550010896F /* STKAutoRecoveringHttpDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STKAutoRecoveringHttpDataSource.h; sourceTree = "<group>"; };
A1E7C4F4188D5E550010896F /* STKAutoRecoveringHttpDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STKAutoRecoveringHttpDataSource.m; sourceTree = "<group>"; };
A1E7C4F3188D5E550010896F /* STKAutoRecoveringHTTPDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STKAutoRecoveringHTTPDataSource.h; sourceTree = "<group>"; };
A1E7C4F4188D5E550010896F /* STKAutoRecoveringHTTPDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STKAutoRecoveringHTTPDataSource.m; sourceTree = "<group>"; };
A1E7C4F5188D5E550010896F /* STKCoreFoundationDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STKCoreFoundationDataSource.h; sourceTree = "<group>"; };
A1E7C4F6188D5E550010896F /* STKCoreFoundationDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STKCoreFoundationDataSource.m; sourceTree = "<group>"; };
A1E7C4F7188D5E550010896F /* STKDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STKDataSource.h; sourceTree = "<group>"; };
A1E7C4F8188D5E550010896F /* STKDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STKDataSource.m; sourceTree = "<group>"; };
A1E7C4F9188D5E550010896F /* STKDataSourceWrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STKDataSourceWrapper.h; sourceTree = "<group>"; };
A1E7C4FA188D5E550010896F /* STKDataSourceWrapper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STKDataSourceWrapper.m; sourceTree = "<group>"; };
A1E7C4FB188D5E550010896F /* STKHttpDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STKHttpDataSource.h; sourceTree = "<group>"; };
A1E7C4FC188D5E550010896F /* STKHttpDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STKHttpDataSource.m; sourceTree = "<group>"; };
A1E7C4FB188D5E550010896F /* STKHTTPDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STKHTTPDataSource.h; sourceTree = "<group>"; };
A1E7C4FC188D5E550010896F /* STKHTTPDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STKHTTPDataSource.m; sourceTree = "<group>"; };
A1E7C4FD188D5E550010896F /* STKLocalFileDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STKLocalFileDataSource.h; sourceTree = "<group>"; };
A1E7C4FE188D5E550010896F /* STKLocalFileDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STKLocalFileDataSource.m; sourceTree = "<group>"; };
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;
};
@@ -325,7 +325,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
IPHONEOS_DEPLOYMENT_TARGET = 6.0;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
};
@@ -357,7 +357,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
IPHONEOS_DEPLOYMENT_TARGET = 6.0;
SDKROOT = iphoneos;
VALIDATE_PRODUCT = YES;
};
@@ -366,6 +366,7 @@
A1E7C4EC188D57F60010896F /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
DSTROOT = /tmp/StreamingKit.dst;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
@@ -382,6 +383,7 @@
A1E7C4ED188D57F60010896F /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
DSTROOT = /tmp/StreamingKit.dst;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
@@ -452,6 +454,7 @@
A1E7C4ED188D57F60010896F /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
A1E7C4EE188D57F60010896F /* Build configuration list for PBXNativeTarget "StreamingKitTests" */ = {
isa = XCConfigurationList;
@@ -460,6 +463,7 @@
A1E7C4F0188D57F60010896F /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
File diff suppressed because it is too large Load Diff
+49 -55
View File
@@ -44,36 +44,37 @@
#include "UIKit/UIApplication.h"
#endif
#define AudioPlayerDefaultNumberOfAudioQueueBuffers (10 * 1024)
typedef enum
{
STKAudioPlayerInternalStateInitialised = 0,
STKAudioPlayerInternalStateRunning = 1,
STKAudioPlayerInternalStatePlaying = (1 << 1) | STKAudioPlayerInternalStateRunning,
STKAudioPlayerInternalStateRebuffering = (1 << 2) | STKAudioPlayerInternalStateRunning,
STKAudioPlayerInternalStateStartingThread = (1 << 3) | STKAudioPlayerInternalStateRunning,
STKAudioPlayerInternalStateWaitingForData = (1 << 4) | STKAudioPlayerInternalStateRunning,
/* Same as STKAudioPlayerInternalStateWaitingForData but isn't immediately raised as a buffering event */
STKAudioPlayerInternalStateWaitingForDataAfterSeek = (1 << 5) | STKAudioPlayerInternalStateRunning,
STKAudioPlayerInternalStatePaused = (1 << 6) | STKAudioPlayerInternalStateRunning,
STKAudioPlayerInternalStateFlushingAndStoppingButStillPlaying = (1 << 7) | STKAudioPlayerInternalStateRunning,
STKAudioPlayerInternalStateStopping = (1 << 8),
STKAudioPlayerInternalStateStopped = (1 << 9),
STKAudioPlayerInternalStateDisposed = (1 << 10),
STKAudioPlayerInternalStateError = (1 << 31)
}
STKAudioPlayerInternalState;
typedef enum
{
AudioPlayerInternalStateInitialised = 0,
AudioPlayerInternalStateRunning = 1,
AudioPlayerInternalStatePlaying = (1 << 1) | AudioPlayerInternalStateRunning,
AudioPlayerInternalStateStartingThread = (1 << 2) | AudioPlayerInternalStateRunning,
AudioPlayerInternalStateWaitingForData = (1 << 3) | AudioPlayerInternalStateRunning,
AudioPlayerInternalStateWaitingForQueueToStart = (1 << 4) | AudioPlayerInternalStateRunning,
AudioPlayerInternalStatePaused = (1 << 5) | AudioPlayerInternalStateRunning,
AudioPlayerInternalStateRebuffering = (1 << 6) | AudioPlayerInternalStateRunning,
AudioPlayerInternalStateStopping = (1 << 7),
AudioPlayerInternalStateStopped = (1 << 8),
AudioPlayerInternalStateDisposed = (1 << 9),
AudioPlayerInternalStateError = (1 << 10)
STKAudioPlayerStateReady,
STKAudioPlayerStateRunning = 1,
STKAudioPlayerStatePlaying = (1 << 1) | STKAudioPlayerStateRunning,
STKAudioPlayerStateBuffering = (1 << 2) | STKAudioPlayerStatePlaying,
STKAudioPlayerStatePaused = (1 << 3) | STKAudioPlayerStateRunning,
STKAudioPlayerStateStopped = (1 << 4),
STKAudioPlayerStateError = (1 << 5),
STKAudioPlayerStateDisposed = (1 << 6)
}
AudioPlayerInternalState;
typedef enum
{
AudioPlayerStateReady,
AudioPlayerStateRunning = 1,
AudioPlayerStatePlaying = (1 << 1) | AudioPlayerStateRunning,
AudioPlayerStatePaused = (1 << 2) | AudioPlayerStateRunning,
AudioPlayerStateStopped = (1 << 3),
AudioPlayerStateError = (1 << 4),
AudioPlayerStateDisposed = (1 << 5)
}
AudioPlayerState;
STKAudioPlayerState;
typedef enum
{
@@ -82,53 +83,45 @@ typedef enum
AudioPlayerStopReasonUserAction,
AudioPlayerStopReasonUserActionFlushStop
}
AudioPlayerStopReason;
STKAudioPlayerStopReason;
typedef enum
{
AudioPlayerErrorNone = 0,
AudioPlayerErrorDataSource,
AudioPlayerErrorStreamParseBytesFailed,
AudioPlayerErrorDataNotFound,
AudioPlayerErrorQueueStartFailed,
AudioPlayerErrorQueuePauseFailed,
AudioPlayerErrorUnknownBuffer,
AudioPlayerErrorQueueStopFailed,
AudioPlayerErrorQueueCreationFailed,
AudioPlayerErrorOther = -1
STKAudioPlayerErrorNone = 0,
STKAudioPlayerErrorDataSource,
STKAudioPlayerErrorStreamParseBytesFailed,
STKAudioPlayerErrorDataNotFound,
STKAudioPlayerErrorQueueStartFailed,
STKAudioPlayerErrorQueuePauseFailed,
STKAudioPlayerErrorUnknownBuffer,
STKAudioPlayerErrorQueueStopFailed,
STKAudioPlayerErrorQueueCreationFailed,
STKAudioPlayerErrorOther = -1
}
AudioPlayerErrorCode;
STKAudioPlayerErrorCode;
@class STKDataSource;
@class STKAudioPlayer;
@protocol STKAudioPlayerDelegate <NSObject>
-(void) audioPlayer:(STKAudioPlayer*)audioPlayer stateChanged:(AudioPlayerState)state;
-(void) audioPlayer:(STKAudioPlayer*)audioPlayer didEncounterError:(AudioPlayerErrorCode)errorCode;
-(void) audioPlayer:(STKAudioPlayer*)audioPlayer stateChanged:(STKAudioPlayerState)state;
-(void) audioPlayer:(STKAudioPlayer*)audioPlayer didEncounterError:(STKAudioPlayerErrorCode)errorCode;
-(void) audioPlayer:(STKAudioPlayer*)audioPlayer didStartPlayingQueueItemId:(NSObject*)queueItemId;
-(void) audioPlayer:(STKAudioPlayer*)audioPlayer didFinishBufferingSourceWithQueueItemId:(NSObject*)queueItemId;
-(void) audioPlayer:(STKAudioPlayer*)audioPlayer didFinishPlayingQueueItemId:(NSObject*)queueItemId withReason:(AudioPlayerStopReason)stopReason andProgress:(double)progress andDuration:(double)duration;
-(void) audioPlayer:(STKAudioPlayer*)audioPlayer didFinishPlayingQueueItemId:(NSObject*)queueItemId withReason:(STKAudioPlayerStopReason)stopReason andProgress:(double)progress andDuration:(double)duration;
@optional
-(void) audioPlayer:(STKAudioPlayer*)audioPlayer logInfo:(NSString*)line;
-(void) audioPlayer:(STKAudioPlayer*)audioPlayer internalStateChanged:(AudioPlayerInternalState)state;
-(void) audioPlayer: (STKAudioPlayer*)audioPlayer didCancelQueuedItems:(NSArray*)queuedItems;
-(void) audioPlayer:(STKAudioPlayer*)audioPlayer internalStateChanged:(STKAudioPlayerInternalState)state;
-(void) audioPlayer:(STKAudioPlayer*)audioPlayer didCancelQueuedItems:(NSArray*)queuedItems;
@end
@class STKQueueEntry;
typedef struct
{
AudioQueueBufferRef ref;
int bufferIndex;
}
AudioQueueBufferRefLookupEntry;
@interface STKAudioPlayer : NSObject<STKDataSourceDelegate>
@property (readonly) double duration;
@property (readonly) double progress;
@property (readwrite) AudioPlayerState state;
@property (readonly) AudioPlayerStopReason stopReason;
@property (readwrite) STKAudioPlayerState state;
@property (readonly) STKAudioPlayerStopReason stopReason;
@property (readwrite, unsafe_unretained) id<STKAudioPlayerDelegate> delegate;
@property (readwrite) BOOL meteringEnabled;
@@ -141,6 +134,7 @@ AudioQueueBufferRefLookupEntry;
-(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;
File diff suppressed because it is too large Load Diff
@@ -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
@@ -40,14 +40,14 @@
#import <netdb.h>
#import <Foundation/Foundation.h>
#import <SystemConfiguration/SystemConfiguration.h>
#import "STKAutoRecoveringHttpDataSource.h"
#import "STKAutoRecoveringHTTPDataSource.h"
#define MAX_IMMEDIATE_RECONNECT_ATTEMPTS (8)
#define MAX_ATTEMPTS_WITH_SERVER_ERROR (MAX_IMMEDIATE_RECONNECT_ATTEMPTS + 2)
#define MAX_IMMEDIATE_RECONNECT_ATTEMPTS (2)
@interface STKAutoRecoveringHttpDataSource()
@interface STKAutoRecoveringHTTPDataSource()
{
int reconnectAttempts;
int serial;
int waitSeconds;
BOOL waitingForNetwork;
SCNetworkReachabilityRef reachabilityRef;
}
@@ -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])
{
@@ -130,7 +130,7 @@ static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReach
if (reachabilityRef != NULL)
{
SCNetworkReachabilitySetCallback(reachabilityRef, NULL, NULL);
SCNetworkReachabilityUnscheduleFromRunLoop(reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
SCNetworkReachabilityUnscheduleFromRunLoop(reachabilityRef, [self.innerDataSource.eventsRunLoop getCFRunLoop], kCFRunLoopDefaultMode);
}
}
@@ -148,7 +148,7 @@ static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReach
-(void) dealloc
{
NSLog(@"STKAutoRecoveringHttpDataSource dealloc");
NSLog(@"STKAutoRecoveringHTTPDataSource dealloc");
self.innerDataSource.delegate = nil;
@@ -168,24 +168,35 @@ static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReach
{
waitingForNetwork = NO;
[self attemptReconnect];
[self attemptReconnectWithSerial:@(serial)];
}
}
-(void) dataSourceDataAvailable:(STKDataSource*)dataSource
{
reconnectAttempts = 0;
serial++;
waitSeconds = 1;
[super dataSourceDataAvailable:dataSource];
}
-(void) attemptReconnect
-(void) attemptReconnectWithSerial:(NSNumber*)serialIn
{
reconnectAttempts++;
if (serialIn.intValue != self->serial)
{
return;
}
NSLog(@"attemptReconnect %lld/%lld", self.position, self.length);
[self seekToOffset:self.position];
}
-(void) attemptReconnectWithTimer:(NSTimer*)timer
{
[self attemptReconnectWithSerial:(NSNumber*)timer.userInfo];
}
-(void) processRetryOnError
{
if (![self hasGotNetworkConnection])
@@ -195,18 +206,20 @@ static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReach
return;
}
if (!(self.innerDataSource.httpStatusCode >= 200 && self.innerDataSource.httpStatusCode <= 299) && reconnectAttempts >= MAX_ATTEMPTS_WITH_SERVER_ERROR)
NSRunLoop* runLoop = self.innerDataSource.eventsRunLoop;
if (runLoop == nil)
{
[super dataSourceErrorOccured:self];
}
else if (reconnectAttempts > MAX_IMMEDIATE_RECONNECT_ATTEMPTS)
{
[self performSelector:@selector(attemptReconnect) withObject:nil afterDelay:5];
[self performSelector:@selector(attemptReconnectWithSerial:) withObject:@(serial) afterDelay:waitSeconds];
}
else
{
[self attemptReconnect];
NSTimer* timer = [NSTimer timerWithTimeInterval:waitSeconds target:self selector:@selector(attemptReconnectWithTimer:) userInfo:@(serial) repeats:NO];
[runLoop addTimer:timer forMode:NSRunLoopCommonModes];
}
waitSeconds = MIN(waitSeconds + 1, 5);
}
-(void) dataSourceEof:(STKDataSource*)dataSource
@@ -223,6 +236,8 @@ static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReach
-(void) dataSourceErrorOccured:(STKDataSource*)dataSource
{
NSLog(@"dataSourceErrorOccured");
if (self.innerDataSource.httpStatusCode == 416 /* Range out of bounds */)
{
[super dataSourceEof:dataSource];
@@ -231,13 +246,11 @@ static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReach
{
[self processRetryOnError];
}
}
-(NSString*) description
{
return [NSString stringWithFormat:@"Auto-recovering HTTP data source with file length: %lld and position: %lld", self.length, self.position];
return [NSString stringWithFormat:@"HTTP data source with file length: %lld and position: %lld", self.length, self.position];
}
@end
@@ -44,14 +44,19 @@
@interface STKCoreFoundationDataSource : STKDataSource
{
@protected
BOOL isInErrorState;
CFReadStreamRef stream;
NSRunLoop* eventsRunLoop;
}
@property (readonly) BOOL isInErrorState;
-(BOOL) reregisterForEvents;
-(void) open;
-(void) dataAvailable;
-(void) eof;
-(void) errorOccured;
-(CFStreamStatus) status;
@end
@@ -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];
}
@@ -98,6 +105,10 @@ static void ReadStreamCallbackProc(CFReadStreamRef stream, CFStreamEventType eve
}
}
-(void) open
{
}
-(void) seekToOffset:(long long)offset
{
}
@@ -133,19 +144,21 @@ static void ReadStreamCallbackProc(CFReadStreamRef stream, CFStreamEventType eve
-(BOOL) registerForEvents:(NSRunLoop*)runLoop
{
eventsRunLoop = runLoop;
if (stream)
{
CFStreamClientContext context = {0, (__bridge void*)self, NULL, NULL, NULL};
CFReadStreamSetClient(stream, kCFStreamEventHasBytesAvailable | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered, ReadStreamCallbackProc, &context);
CFReadStreamScheduleWithRunLoop(stream, [eventsRunLoop getCFRunLoop], kCFRunLoopCommonModes);
if (!stream)
{
[self open];
return YES;
}
CFStreamClientContext context = {0, (__bridge void*)self, NULL, NULL, NULL};
return NO;
CFReadStreamSetClient(stream, kCFStreamEventHasBytesAvailable | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered, ReadStreamCallbackProc, &context);
CFReadStreamScheduleWithRunLoop(stream, [eventsRunLoop getCFRunLoop], kCFRunLoopCommonModes);
return YES;
}
-(BOOL) hasBytesAvailable
@@ -158,4 +171,14 @@ static void ReadStreamCallbackProc(CFReadStreamRef stream, CFStreamEventType eve
return CFReadStreamHasBytesAvailable(stream);
}
-(CFStreamStatus) status
{
if (stream)
{
return CFReadStreamGetStatus(stream);
}
return 0;
}
@end
@@ -34,15 +34,21 @@
#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;
-(NSRunLoop*) eventsRunLoop;
@end
@@ -32,18 +32,18 @@
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;
int relativePosition;
long long seekStart;
long long relativePosition;
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,9 +76,7 @@
relativePosition = 0;
fileLength = -1;
self->urlProvider = urlProviderIn;
[self open];
self->asyncUrlProvider = [asyncUrlProviderIn copy];
audioFileTypeHint = [STKLocalFileDataSource audioFileTypeHintFromFileExtension:self->currentUrl.pathExtension];
}
@@ -78,7 +86,7 @@
-(void) dealloc
{
NSLog(@"STKHttpDataSource dealloc");
NSLog(@"STKHTTPDataSource dealloc");
}
-(NSURL*) url
@@ -148,7 +156,7 @@
}
NSString* contentType = [httpHeaders objectForKey:@"Content-Type"];
AudioFileTypeID typeIdFromMimeType = [STKHttpDataSource audioFileTypeHintFromMimeType:contentType];
AudioFileTypeID typeIdFromMimeType = [STKHTTPDataSource audioFileTypeHintFromMimeType:contentType];
if (typeIdFromMimeType != 0)
{
@@ -189,12 +197,15 @@
CFRelease(stream);
}
NSAssert([NSRunLoop currentRunLoop] == eventsRunLoop, @"Seek called on wrong thread");
stream = 0;
relativePosition = 0;
seekStart = (int)offset;
seekStart = offset;
[self open];
[self reregisterForEvents];
self->isInErrorState = NO;
[self openForSeek:YES];
}
-(int) readIntoBuffer:(UInt8*)buffer withSize:(int)size
@@ -218,75 +229,97 @@
-(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=%lld-", 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);
}
[self reregisterForEvents];
// Open
if (!CFReadStreamOpen(stream))
{
CFRelease(stream);
CFRelease(message);
[self errorOccured];
return;
}
self->isInErrorState = NO;
CFRelease(message);
});
}
-(NSRunLoop*) eventsRunLoop
{
return self->eventsRunLoop;
}
-(NSString*) description
{
return [NSString stringWithFormat:@"HTTP data source with file length: %lld and position: %lld", self.length, self.position];
}
@@ -53,8 +53,6 @@
{
self.filePath = filePathIn;
[self open];
audioFileTypeHint = [STKLocalFileDataSource audioFileTypeHintFromFileExtension:filePathIn.pathExtension];
}
@@ -107,6 +105,8 @@
{
if (stream)
{
[self unregisterForEvents];
CFReadStreamClose(stream);
stream = 0;
@@ -115,26 +115,23 @@
-(void) open
{
NSURL* url = [[NSURL alloc] initFileURLWithPath:self.filePath];
if (stream)
{
[self unregisterForEvents];
CFReadStreamClose(stream);
CFRelease(stream);
stream = 0;
}
NSURL* url = [[NSURL alloc] initFileURLWithPath:self.filePath];
stream = CFReadStreamCreateWithFile(NULL, (__bridge CFURLRef)url);
NSError *fileError;
NSFileManager *manager = [[NSFileManager alloc] init];
NSString *path = [NSString stringWithUTF8String:[url fileSystemRepresentation]];
NSDictionary *attributes = [manager attributesOfItemAtPath:path
error:&fileError];
NSError* fileError;
NSFileManager* manager = [[NSFileManager alloc] init];
NSDictionary* attributes = [manager attributesOfItemAtPath:filePath error:&fileError];
if (fileError)
{
@@ -145,13 +142,14 @@
}
NSNumber* number = [attributes objectForKey:@"NSFileSize"];
if (number)
{
length = number.longLongValue;
}
[self reregisterForEvents];
CFReadStreamOpen(stream);
}
@@ -200,7 +198,6 @@
[self close];
[self open];
[self reregisterForEvents];
}
if (CFReadStreamSetProperty(stream, kCFStreamPropertyFileCurrentOffset, (__bridge CFTypeRef)[NSNumber numberWithLongLong:offset]) != TRUE)