Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c9ba30b692 | |||
| c68384ec50 | |||
| 756504f33b | |||
| 894dce8761 | |||
| c3a35fcdf3 | |||
| ce3f938309 | |||
| 429c156d8b | |||
| 0eb7687a63 | |||
| 9b952de734 | |||
| 93e6f4b6d9 | |||
| 9ed755cda4 | |||
| 7cc3f85a59 | |||
| a93d6867c5 | |||
| 4660f0ce80 |
@@ -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 */; };
|
||||
A1EBEE64188DE34500681B04 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1EBEE63188DE34500681B04 /* SystemConfiguration.framework */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@@ -58,6 +59,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>"; };
|
||||
A1EBEE63188DE34500681B04 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@@ -65,6 +67,7 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
A1EBEE64188DE34500681B04 /* SystemConfiguration.framework in Frameworks */,
|
||||
A1115964188D691500641365 /* libStreamingKit.a in Frameworks */,
|
||||
A1115937188D686000641365 /* CoreGraphics.framework in Frameworks */,
|
||||
A1115939188D686000641365 /* UIKit.framework in Frameworks */,
|
||||
@@ -107,6 +110,7 @@
|
||||
A1115933188D686000641365 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A1EBEE63188DE34500681B04 /* SystemConfiguration.framework */,
|
||||
A1115963188D691500641365 /* libStreamingKit.a */,
|
||||
A1115934188D686000641365 /* Foundation.framework */,
|
||||
A1115936188D686000641365 /* CoreGraphics.framework */,
|
||||
@@ -397,15 +401,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";
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(USER_LIBRARY_DIR)/Developer/Xcode/DerivedData/StreamingKit-dabtccqthoyywldsgcvllbcjcmma/Build/Products/Debug-iphoneos",
|
||||
);
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 6.0;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
WRAPPER_EXTENSION = app;
|
||||
};
|
||||
@@ -414,15 +416,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";
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(USER_LIBRARY_DIR)/Developer/Xcode/DerivedData/StreamingKit-dabtccqthoyywldsgcvllbcjcmma/Build/Products/Debug-iphoneos",
|
||||
);
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 6.0;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
WRAPPER_EXTENSION = app;
|
||||
};
|
||||
|
||||
@@ -9,7 +9,9 @@
|
||||
#import "AppDelegate.h"
|
||||
#import "STKAudioPlayer.h"
|
||||
#import "AudioPlayerView.h"
|
||||
#import "STKAutoRecoveringHttpDataSource.h"
|
||||
#import "SampleQueueId.h"
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
|
||||
@interface AppDelegate()
|
||||
{
|
||||
@@ -22,6 +24,10 @@
|
||||
|
||||
-(BOOL) application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
|
||||
{
|
||||
NSError* error;
|
||||
|
||||
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&error];
|
||||
|
||||
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
|
||||
|
||||
self.window.backgroundColor = [UIColor whiteColor];
|
||||
@@ -43,7 +49,9 @@
|
||||
{
|
||||
NSURL* url = [NSURL URLWithString:@"http://fs.bloom.fm/oss/audiosamples/sample.mp3"];
|
||||
|
||||
[audioPlayer setDataSource:[audioPlayer dataSourceFromURL:url] withQueueItemId:[[SampleQueueId alloc] initWithUrl:url andCount:0]];
|
||||
STKAutoRecoveringHttpDataSource* dataSource = [[STKAutoRecoveringHttpDataSource alloc] initWithHttpDataSource:(STKHttpDataSource*)[audioPlayer dataSourceFromURL:url]];
|
||||
|
||||
[audioPlayer setDataSource:dataSource withQueueItemId:[[SampleQueueId alloc] initWithUrl:url andCount:0]];
|
||||
}
|
||||
|
||||
-(void) audioPlayerViewPlayFromLocalFileSelected:(AudioPlayerView*)audioPlayerView
|
||||
|
||||
@@ -24,6 +24,10 @@
|
||||
<string>1.0</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>UIBackgroundModes</key>
|
||||
<array>
|
||||
<string>audio</string>
|
||||
</array>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>armv7</string>
|
||||
|
||||
@@ -10,7 +10,7 @@ The primary motivation of this project was to decouple the input data sources fr
|
||||
* Easy to read source
|
||||
* Mostly asynchronous API
|
||||
* Buffered and gapless playback
|
||||
* Easy to implement audio data sources (HTTP and local file system DataSources provided)
|
||||
* Easy to implement audio data sources (Local, HTTP, Auto Recovering HTTP DataSources are provided)
|
||||
* Easy to extend DataSource to support adaptive buffering, encryption, etc.
|
||||
* Optimised for low CPU/battery usage
|
||||
|
||||
@@ -20,25 +20,22 @@ StreamingKit is also available as a [Cocoapod](http://cocoapods.org/?q=Streaming
|
||||
|
||||
## Example
|
||||
|
||||
There are two main classes. The `STKDataSource` class which is the abstract base class for the various compressed audio data sources (HTTP, local file are provided). The `STKAudioPlayer` class manages and renders audio from a queue DataSources.
|
||||
There are two main classes. The `STKDataSource` class which is the abstract base class for the various compressed audio data sources. The `STKAudioPlayer` class manages and renders audio from a queue DataSources. By default `STKAudioPlayer` will automatically parse URLs and create the appropriate data source internally.
|
||||
|
||||
```objective-c
|
||||
|
||||
// Create AudioPlayer
|
||||
|
||||
STKAudioPlayer* audioPlayer = [[STKAudioPlayer alloc] init];
|
||||
audioPlayer.delegate = self;
|
||||
|
||||
// Queue on a URL to play. Each queue item has a unique ID (item1) that to identify the related file in delegate callbacks
|
||||
|
||||
[audioPlayer setDataSource:[audioPlayer dataSourceFromURL:@"http://fs.bloom.fm/oss/audiosamples/sample.mp3"] withQueueItemId:@"item1"];
|
||||
[audioPlayer play:@"http://fs.bloom.fm/oss/audiosamples/sample.mp3"];
|
||||
|
||||
|
||||
```
|
||||
|
||||
## Background Playback
|
||||
## More
|
||||
|
||||
Background playback on iOS is easily added to your application by calling the `AudioSessionInitialize` in your AppDelegate and adding audio to the `UIBackgroundModes` key in your plist file.
|
||||
More documentation is available on the project [wiki](https://github.com/tumtumtum/StreamingKit/wiki)
|
||||
|
||||
### Authors and Contributors
|
||||
Copyright (c) 2012-2014, Thong Nguyen (@tumtumtum)
|
||||
|
||||
@@ -5,36 +5,36 @@
|
||||
<key>IDESourceControlProjectFavoriteDictionaryKey</key>
|
||||
<false/>
|
||||
<key>IDESourceControlProjectIdentifier</key>
|
||||
<string>A5188B80-95E0-46CF-BEC9-273724D5616A</string>
|
||||
<string>64C9BE2D-7DAD-476A-B9AC-1C4603CD4124</string>
|
||||
<key>IDESourceControlProjectName</key>
|
||||
<string>StreamingKit</string>
|
||||
<key>IDESourceControlProjectOriginsDictionary</key>
|
||||
<dict>
|
||||
<key>821E27F3-2BD4-4B06-BB39-F4C3ECC1BBAD</key>
|
||||
<string>https://github.com/tumtumtum/audjustable.git</string>
|
||||
<key>DD310C30-B3D0-4BD7-9565-9F29F09CC4F8</key>
|
||||
<string>https://github.com/tumtumtum/StreamingKit.git</string>
|
||||
</dict>
|
||||
<key>IDESourceControlProjectPath</key>
|
||||
<string>StreamingKit.xcworkspace</string>
|
||||
<key>IDESourceControlProjectRelativeInstallPathDictionary</key>
|
||||
<dict>
|
||||
<key>821E27F3-2BD4-4B06-BB39-F4C3ECC1BBAD</key>
|
||||
<key>DD310C30-B3D0-4BD7-9565-9F29F09CC4F8</key>
|
||||
<string>..</string>
|
||||
</dict>
|
||||
<key>IDESourceControlProjectURL</key>
|
||||
<string>https://github.com/tumtumtum/audjustable.git</string>
|
||||
<string>https://github.com/tumtumtum/StreamingKit.git</string>
|
||||
<key>IDESourceControlProjectVersion</key>
|
||||
<integer>110</integer>
|
||||
<key>IDESourceControlProjectWCCIdentifier</key>
|
||||
<string>821E27F3-2BD4-4B06-BB39-F4C3ECC1BBAD</string>
|
||||
<string>DD310C30-B3D0-4BD7-9565-9F29F09CC4F8</string>
|
||||
<key>IDESourceControlProjectWCConfigurations</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
||||
<string>public.vcs.git</string>
|
||||
<key>IDESourceControlWCCIdentifierKey</key>
|
||||
<string>821E27F3-2BD4-4B06-BB39-F4C3ECC1BBAD</string>
|
||||
<string>DD310C30-B3D0-4BD7-9565-9F29F09CC4F8</string>
|
||||
<key>IDESourceControlWCCName</key>
|
||||
<string>audjustable</string>
|
||||
<string>StreamingKit</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
|
||||
@@ -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)";
|
||||
DSTROOT = /tmp/StreamingKit.dst;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
@@ -382,6 +383,7 @@
|
||||
A1E7C4ED188D57F60010896F /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ARCHS = "$(ARCHS_STANDARD)";
|
||||
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 */
|
||||
};
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
#include "UIKit/UIApplication.h"
|
||||
#endif
|
||||
|
||||
#define AudioPlayerDefaultNumberOfAudioQueueBuffers (2 * 1024)
|
||||
#define AudioPlayerDefaultNumberOfAudioQueueBuffers (10 * 1024)
|
||||
|
||||
typedef enum
|
||||
{
|
||||
@@ -94,10 +94,12 @@ typedef enum
|
||||
AudioPlayerErrorQueuePauseFailed,
|
||||
AudioPlayerErrorUnknownBuffer,
|
||||
AudioPlayerErrorQueueStopFailed,
|
||||
AudioPlayerErrorOther
|
||||
AudioPlayerErrorQueueCreationFailed,
|
||||
AudioPlayerErrorOther = -1
|
||||
}
|
||||
AudioPlayerErrorCode;
|
||||
|
||||
@class STKDataSource;
|
||||
@class STKAudioPlayer;
|
||||
|
||||
@protocol STKAudioPlayerDelegate <NSObject>
|
||||
@@ -112,7 +114,7 @@ AudioPlayerErrorCode;
|
||||
-(void) audioPlayer: (STKAudioPlayer*)audioPlayer didCancelQueuedItems:(NSArray*)queuedItems;
|
||||
@end
|
||||
|
||||
@class QueueEntry;
|
||||
@class STKQueueEntry;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
@@ -122,68 +124,6 @@ typedef struct
|
||||
AudioQueueBufferRefLookupEntry;
|
||||
|
||||
@interface STKAudioPlayer : NSObject<STKDataSourceDelegate>
|
||||
{
|
||||
@private
|
||||
UInt8* readBuffer;
|
||||
int readBufferSize;
|
||||
|
||||
NSOperationQueue* fastApiQueue;
|
||||
|
||||
QueueEntry* currentlyPlayingEntry;
|
||||
QueueEntry* 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 fillBufferIndex;
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
UIBackgroundTaskIdentifier backgroundTaskId;
|
||||
#endif
|
||||
|
||||
AudioPlayerErrorCode errorCode;
|
||||
AudioPlayerStopReason stopReason;
|
||||
|
||||
int currentlyPlayingLock;
|
||||
pthread_mutex_t playerMutex;
|
||||
pthread_mutex_t queueBuffersMutex;
|
||||
pthread_cond_t queueBufferReadyCondition;
|
||||
|
||||
volatile BOOL waiting;
|
||||
volatile BOOL disposeWasRequested;
|
||||
volatile BOOL seekToTimeWasRequested;
|
||||
volatile BOOL newFileToPlay;
|
||||
volatile double requestedSeekTime;
|
||||
volatile BOOL audioQueueFlushing;
|
||||
volatile SInt64 audioPacketsReadCount;
|
||||
volatile SInt64 audioPacketsPlayedCount;
|
||||
|
||||
BOOL meteringEnabled;
|
||||
AudioQueueLevelMeterState* levelMeterState;
|
||||
NSInteger numberOfChannels;
|
||||
}
|
||||
|
||||
@property (readonly) double duration;
|
||||
@property (readonly) double progress;
|
||||
@@ -194,10 +134,12 @@ AudioQueueBufferRefLookupEntry;
|
||||
|
||||
-(id) init;
|
||||
-(id) initWithNumberOfAudioQueueBuffers:(int)numberOfAudioQueueBuffers andReadBufferSize:(int)readBufferSizeIn;
|
||||
-(DataSource*) dataSourceFromURL:(NSURL*)url;
|
||||
-(void) play:(NSURL*)url;
|
||||
-(void) queueDataSource:(DataSource*)dataSource withQueueItemId:(NSObject*)queueItemId;
|
||||
-(void) setDataSource:(DataSource*)dataSourceIn withQueueItemId:(NSObject*)queueItemId;
|
||||
-(STKDataSource*) dataSourceFromURL:(NSURL*)url;
|
||||
-(void) play:(NSString*)urlString;
|
||||
-(void) playWithURL:(NSURL*)url;
|
||||
-(void) playWithDataSource:(STKDataSource*)dataSource;
|
||||
-(void) queueDataSource:(STKDataSource*)dataSource withQueueItemId:(NSObject*)queueItemId;
|
||||
-(void) setDataSource:(STKDataSource*)dataSourceIn withQueueItemId:(NSObject*)queueItemId;
|
||||
-(void) seekToTime:(double)value;
|
||||
-(void) pause;
|
||||
-(void) resume;
|
||||
|
||||
@@ -97,7 +97,7 @@
|
||||
|
||||
@end
|
||||
|
||||
@interface QueueEntry : NSObject
|
||||
@interface STKQueueEntry : NSObject
|
||||
{
|
||||
@public
|
||||
BOOL parsedHeader;
|
||||
@@ -114,7 +114,7 @@
|
||||
AudioStreamBasicDescription audioStreamBasicDescription;
|
||||
}
|
||||
@property (readwrite, retain) NSObject* queueItemId;
|
||||
@property (readwrite, retain) DataSource* dataSource;
|
||||
@property (readwrite, retain) STKDataSource* dataSource;
|
||||
@property (readwrite) int bufferIndex;
|
||||
@property (readonly) UInt64 audioDataLengthInBytes;
|
||||
|
||||
@@ -122,20 +122,20 @@
|
||||
-(double) calculatedBitRate;
|
||||
-(double) progress;
|
||||
|
||||
-(id) initWithDataSource:(DataSource*)dataSource andQueueItemId:(NSObject*)queueItemId;
|
||||
-(id) initWithDataSource:(DataSource*)dataSource andQueueItemId:(NSObject*)queueItemId andBufferIndex:(int)bufferIndex;
|
||||
-(id) initWithDataSource:(STKDataSource*)dataSource andQueueItemId:(NSObject*)queueItemId;
|
||||
-(id) initWithDataSource:(STKDataSource*)dataSource andQueueItemId:(NSObject*)queueItemId andBufferIndex:(int)bufferIndex;
|
||||
|
||||
@end
|
||||
|
||||
@implementation QueueEntry
|
||||
@implementation STKQueueEntry
|
||||
@synthesize dataSource, queueItemId, bufferIndex;
|
||||
|
||||
-(id) initWithDataSource:(DataSource*)dataSourceIn andQueueItemId:(NSObject*)queueItemIdIn
|
||||
-(id) initWithDataSource:(STKDataSource*)dataSourceIn andQueueItemId:(NSObject*)queueItemIdIn
|
||||
{
|
||||
return [self initWithDataSource:dataSourceIn andQueueItemId:queueItemIdIn andBufferIndex:-1];
|
||||
}
|
||||
|
||||
-(id) initWithDataSource:(DataSource*)dataSourceIn andQueueItemId:(NSObject*)queueItemIdIn andBufferIndex:(int)bufferIndexIn
|
||||
-(id) initWithDataSource:(STKDataSource*)dataSourceIn andQueueItemId:(NSObject*)queueItemIdIn andBufferIndex:(int)bufferIndexIn
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
@@ -246,6 +246,68 @@
|
||||
@end
|
||||
|
||||
@interface STKAudioPlayer()
|
||||
{
|
||||
UInt8* readBuffer;
|
||||
int readBufferSize;
|
||||
|
||||
NSOperationQueue* fastApiQueue;
|
||||
|
||||
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 fillBufferIndex;
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
UIBackgroundTaskIdentifier backgroundTaskId;
|
||||
#endif
|
||||
|
||||
AudioPlayerErrorCode errorCode;
|
||||
AudioPlayerStopReason stopReason;
|
||||
|
||||
int currentlyPlayingLock;
|
||||
pthread_mutex_t playerMutex;
|
||||
pthread_mutex_t queueBuffersMutex;
|
||||
pthread_cond_t queueBufferReadyCondition;
|
||||
|
||||
volatile BOOL waiting;
|
||||
volatile BOOL disposeWasRequested;
|
||||
volatile BOOL seekToTimeWasRequested;
|
||||
volatile BOOL newFileToPlay;
|
||||
volatile double requestedSeekTime;
|
||||
volatile BOOL audioQueueFlushing;
|
||||
volatile SInt64 audioPacketsReadCount;
|
||||
volatile SInt64 audioPacketsPlayedCount;
|
||||
|
||||
BOOL meteringEnabled;
|
||||
AudioQueueLevelMeterState* levelMeterState;
|
||||
NSInteger numberOfChannels;
|
||||
}
|
||||
|
||||
@property (readwrite) AudioPlayerInternalState internalState;
|
||||
|
||||
-(void) logInfo:(NSString*)line;
|
||||
@@ -257,11 +319,11 @@
|
||||
-(void) stopAudioQueueWithReason:(NSString*)reason;
|
||||
-(BOOL) processRunloop;
|
||||
-(void) wakeupPlaybackThread;
|
||||
-(void) audioQueueFinishedPlaying:(QueueEntry*)entry;
|
||||
-(void) audioQueueFinishedPlaying:(STKQueueEntry*)entry;
|
||||
-(void) processSeekToTime;
|
||||
-(void) didEncounterError:(AudioPlayerErrorCode)errorCode;
|
||||
-(void) setInternalState:(AudioPlayerInternalState)value;
|
||||
-(void) processDidFinishPlaying:(QueueEntry*)entry withNext:(QueueEntry*)next;
|
||||
-(void) processDidFinishPlaying:(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;
|
||||
@@ -316,9 +378,9 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
|
||||
if ([self.delegate respondsToSelector:@selector(audioPlayer:internalStateChanged:)])
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
[self.delegate audioPlayer:self internalStateChanged:internalState];
|
||||
});
|
||||
{
|
||||
[self.delegate audioPlayer:self internalStateChanged:internalState];
|
||||
});
|
||||
}
|
||||
|
||||
AudioPlayerState newState;
|
||||
@@ -500,17 +562,17 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
|
||||
#endif
|
||||
}
|
||||
|
||||
-(DataSource*) dataSourceFromURL:(NSURL*)url
|
||||
-(STKDataSource*) dataSourceFromURL:(NSURL*)url
|
||||
{
|
||||
DataSource* retval;
|
||||
STKDataSource* retval;
|
||||
|
||||
if ([url.scheme isEqualToString:@"file"])
|
||||
{
|
||||
retval = [[LocalFileDataSource alloc] initWithFilePath:url.path];
|
||||
retval = [[STKLocalFileDataSource alloc] initWithFilePath:url.path];
|
||||
}
|
||||
else
|
||||
{
|
||||
retval = [[HttpDataSource alloc] initWithURL:url];
|
||||
retval = [[STKHttpDataSource alloc] initWithURL:url];
|
||||
}
|
||||
|
||||
return retval;
|
||||
@@ -522,7 +584,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
|
||||
{
|
||||
NSMutableArray* array = [[NSMutableArray alloc] initWithCapacity:bufferingQueue.count + upcomingQueue.count];
|
||||
|
||||
QueueEntry* entry = [bufferingQueue dequeue];
|
||||
STKQueueEntry* entry = [bufferingQueue dequeue];
|
||||
|
||||
if (entry && entry != currentlyPlayingEntry)
|
||||
{
|
||||
@@ -531,10 +593,15 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
|
||||
|
||||
while (bufferingQueue.count > 0)
|
||||
{
|
||||
[array addObject:[[bufferingQueue dequeue] queueItemId]];
|
||||
id queueItemId = [[bufferingQueue dequeue] queueItemId];
|
||||
|
||||
if (queueItemId != nil)
|
||||
{
|
||||
[array addObject:queueItemId];
|
||||
}
|
||||
}
|
||||
|
||||
for (QueueEntry* entry in upcomingQueue)
|
||||
for (STKQueueEntry* entry in upcomingQueue)
|
||||
{
|
||||
[array addObject:entry.queueItemId];
|
||||
}
|
||||
@@ -542,22 +609,35 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
|
||||
[upcomingQueue removeAllObjects];
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
if ([self.delegate respondsToSelector:@selector(audioPlayer:didCancelQueuedItems:)])
|
||||
{
|
||||
[self.delegate audioPlayer:self didCancelQueuedItems:array];
|
||||
}
|
||||
});
|
||||
{
|
||||
if ([self.delegate respondsToSelector:@selector(audioPlayer:didCancelQueuedItems:)])
|
||||
{
|
||||
[self.delegate audioPlayer:self didCancelQueuedItems:array];
|
||||
}
|
||||
});
|
||||
}
|
||||
pthread_mutex_unlock(&playerMutex);
|
||||
}
|
||||
|
||||
-(void) play:(NSURL*)url
|
||||
-(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) setDataSource:(DataSource*)dataSourceIn withQueueItemId:(NSObject*)queueItemId
|
||||
-(void) playWithDataSource:(STKDataSource*)dataSource
|
||||
{
|
||||
[self setDataSource:dataSource withQueueItemId:dataSource];
|
||||
}
|
||||
|
||||
-(void) setDataSource:(STKDataSource*)dataSourceIn withQueueItemId:(NSObject*)queueItemId
|
||||
{
|
||||
[fastApiQueue cancelAllOperations];
|
||||
|
||||
@@ -569,7 +649,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
|
||||
|
||||
[self clearQueue];
|
||||
|
||||
[upcomingQueue enqueue:[[QueueEntry alloc] initWithDataSource:dataSourceIn andQueueItemId:queueItemId]];
|
||||
[upcomingQueue enqueue:[[STKQueueEntry alloc] initWithDataSource:dataSourceIn andQueueItemId:queueItemId]];
|
||||
|
||||
self.internalState = AudioPlayerInternalStateRunning;
|
||||
[self processQueue:YES];
|
||||
@@ -578,13 +658,13 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
|
||||
}];
|
||||
}
|
||||
|
||||
-(void) queueDataSource:(DataSource*)dataSourceIn withQueueItemId:(NSObject*)queueItemId
|
||||
-(void) queueDataSource:(STKDataSource*)dataSourceIn withQueueItemId:(NSObject*)queueItemId
|
||||
{
|
||||
[fastApiQueue addOperationWithBlock:^
|
||||
{
|
||||
pthread_mutex_lock(&playerMutex);
|
||||
{
|
||||
[upcomingQueue enqueue:[[QueueEntry alloc] initWithDataSource:dataSourceIn andQueueItemId:queueItemId]];
|
||||
[upcomingQueue enqueue:[[STKQueueEntry alloc] initWithDataSource:dataSourceIn andQueueItemId:queueItemId]];
|
||||
|
||||
[self processQueue:NO];
|
||||
}
|
||||
@@ -717,12 +797,22 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
|
||||
if (audioQueue == nil)
|
||||
{
|
||||
[self createAudioQueue];
|
||||
|
||||
if (audioQueue == nil)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (memcmp(¤tAudioStreamBasicDescription, ¤tlyReadingEntry->audioStreamBasicDescription, sizeof(currentAudioStreamBasicDescription)) != 0)
|
||||
{
|
||||
if (currentlyReadingEntry == currentlyPlayingEntry)
|
||||
{
|
||||
[self createAudioQueue];
|
||||
|
||||
if (audioQueue == nil)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -774,7 +864,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
|
||||
}
|
||||
|
||||
AudioQueueBufferRef bufferToFill = audioQueueBuffer[fillBufferIndex];
|
||||
memcpy((char*)bufferToFill->mAudioData + bytesFilled, (const char*)inputData + packetOffset, packetSize);
|
||||
memcpy((char*)bufferToFill->mAudioData + bytesFilled, (const char*)inputData + packetOffset, (unsigned long)packetSize);
|
||||
|
||||
packetDescs[packetsFilled] = packetDescriptionsIn[i];
|
||||
packetDescs[packetsFilled].mStartOffset = bytesFilled;
|
||||
@@ -858,7 +948,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
|
||||
return;
|
||||
}
|
||||
|
||||
QueueEntry* entry = nil;
|
||||
STKQueueEntry* entry = nil;
|
||||
|
||||
if (currentlyPlayingEntry)
|
||||
{
|
||||
@@ -1140,6 +1230,11 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
|
||||
|
||||
if (error)
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
[self.delegate audioPlayer:self didEncounterError:AudioPlayerErrorQueueCreationFailed];
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1147,6 +1242,8 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
|
||||
|
||||
if (error)
|
||||
{
|
||||
[self.delegate audioPlayer:self didEncounterError:AudioPlayerErrorQueueCreationFailed];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1188,6 +1285,11 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
|
||||
|
||||
if (error)
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
[self.delegate audioPlayer:self didEncounterError:AudioPlayerErrorQueueCreationFailed];
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -1204,6 +1306,11 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
|
||||
|
||||
if (error)
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
[self.delegate audioPlayer:self didEncounterError:AudioPlayerErrorQueueCreationFailed];
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1224,6 +1331,11 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
|
||||
{
|
||||
free(cookieData);
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
[self.delegate audioPlayer:self didEncounterError:AudioPlayerErrorQueueCreationFailed];
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1245,7 +1357,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
|
||||
|
||||
OSSpinLockLock(¤tlyPlayingLock);
|
||||
|
||||
QueueEntry* entry = currentlyPlayingEntry;
|
||||
STKQueueEntry* entry = currentlyPlayingEntry;
|
||||
|
||||
if (entry == nil)
|
||||
{
|
||||
@@ -1275,7 +1387,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
|
||||
|
||||
OSSpinLockLock(¤tlyPlayingLock);
|
||||
|
||||
QueueEntry* entry = currentlyPlayingEntry;
|
||||
STKQueueEntry* entry = currentlyPlayingEntry;
|
||||
|
||||
if (entry == nil)
|
||||
{
|
||||
@@ -1358,7 +1470,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
|
||||
}
|
||||
}
|
||||
|
||||
-(void) setCurrentlyReadingEntry:(QueueEntry*)entry andStartPlaying:(BOOL)startPlaying
|
||||
-(void) setCurrentlyReadingEntry:(STKQueueEntry*)entry andStartPlaying:(BOOL)startPlaying
|
||||
{
|
||||
pthread_mutex_lock(&queueBuffersMutex);
|
||||
|
||||
@@ -1412,13 +1524,13 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
|
||||
pthread_mutex_unlock(&queueBuffersMutex);
|
||||
}
|
||||
|
||||
-(void) audioQueueFinishedPlaying:(QueueEntry*)entry
|
||||
-(void) audioQueueFinishedPlaying:(STKQueueEntry*)entry
|
||||
{
|
||||
pthread_mutex_lock(&playerMutex);
|
||||
{
|
||||
pthread_mutex_lock(&queueBuffersMutex);
|
||||
{
|
||||
QueueEntry* next = [bufferingQueue dequeue];
|
||||
STKQueueEntry* next = [bufferingQueue dequeue];
|
||||
|
||||
[self processDidFinishPlaying:entry withNext:next];
|
||||
}
|
||||
@@ -1427,7 +1539,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
|
||||
pthread_mutex_unlock(&playerMutex);
|
||||
}
|
||||
|
||||
-(void) processDidFinishPlaying:(QueueEntry*)entry withNext:(QueueEntry*)next
|
||||
-(void) processDidFinishPlaying:(STKQueueEntry*)entry withNext:(STKQueueEntry*)next
|
||||
{
|
||||
if (entry != currentlyPlayingEntry)
|
||||
{
|
||||
@@ -1508,7 +1620,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
|
||||
}
|
||||
else if (newFileToPlay)
|
||||
{
|
||||
QueueEntry* entry = [upcomingQueue dequeue];
|
||||
STKQueueEntry* entry = [upcomingQueue dequeue];
|
||||
|
||||
self.internalState = AudioPlayerInternalStateWaitingForData;
|
||||
|
||||
@@ -1584,7 +1696,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
|
||||
{
|
||||
BOOL nextIsIncompatible = NO;
|
||||
|
||||
QueueEntry* next = [bufferingQueue peek];
|
||||
STKQueueEntry* next = [bufferingQueue peek];
|
||||
|
||||
if (next == nil)
|
||||
{
|
||||
@@ -1610,7 +1722,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
|
||||
{
|
||||
if (upcomingQueue.count > 0)
|
||||
{
|
||||
QueueEntry* entry = [upcomingQueue dequeue];
|
||||
STKQueueEntry* entry = [upcomingQueue dequeue];
|
||||
|
||||
BOOL startPlaying = currentlyPlayingEntry == nil;
|
||||
BOOL wasCurrentlyPlayingNothing = currentlyPlayingEntry == nil;
|
||||
@@ -1655,7 +1767,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
|
||||
[bufferingQueue dequeue];
|
||||
}
|
||||
|
||||
QueueEntry* newEntry = [[QueueEntry alloc] initWithDataSource:currentlyReadingEntry.dataSource andQueueItemId:currentlyReadingEntry.queueItemId];
|
||||
STKQueueEntry* newEntry = [[STKQueueEntry alloc] initWithDataSource:currentlyReadingEntry.dataSource andQueueItemId:currentlyReadingEntry.queueItemId];
|
||||
|
||||
newEntry->audioStreamBasicDescription = currentlyReadingEntry->audioStreamBasicDescription;
|
||||
|
||||
@@ -1730,7 +1842,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
|
||||
{
|
||||
OSStatus error;
|
||||
OSSpinLockLock(¤tlyPlayingLock);
|
||||
QueueEntry* currentEntry = currentlyReadingEntry;
|
||||
STKQueueEntry* currentEntry = currentlyReadingEntry;
|
||||
OSSpinLockUnlock(¤tlyPlayingLock);
|
||||
|
||||
NSAssert(currentEntry == currentlyPlayingEntry, @"playing and reading must be the same");
|
||||
@@ -1811,9 +1923,12 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
|
||||
[self stopAudioQueueWithReason:@"from startAudioQueue"];
|
||||
[self createAudioQueue];
|
||||
|
||||
self.internalState = AudioPlayerInternalStateWaitingForQueueToStart;
|
||||
|
||||
AudioQueueStart(audioQueue, NULL);
|
||||
if (audioQueue != nil)
|
||||
{
|
||||
self.internalState = AudioPlayerInternalStateWaitingForQueueToStart;
|
||||
|
||||
AudioQueueStart(audioQueue, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
[self stopSystemBackgroundTask];
|
||||
@@ -1926,7 +2041,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
|
||||
pthread_mutex_unlock(&queueBuffersMutex);
|
||||
}
|
||||
|
||||
-(void) dataSourceDataAvailable:(DataSource*)dataSourceIn
|
||||
-(void) dataSourceDataAvailable:(STKDataSource*)dataSourceIn
|
||||
{
|
||||
OSStatus error;
|
||||
|
||||
@@ -1989,7 +2104,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
|
||||
}
|
||||
}
|
||||
|
||||
-(void) dataSourceErrorOccured:(DataSource*)dataSourceIn
|
||||
-(void) dataSourceErrorOccured:(STKDataSource*)dataSourceIn
|
||||
{
|
||||
if (currentlyReadingEntry.dataSource != dataSourceIn)
|
||||
{
|
||||
@@ -1999,7 +2114,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
|
||||
[self didEncounterError:AudioPlayerErrorDataNotFound];
|
||||
}
|
||||
|
||||
-(void) dataSourceEof:(DataSource*)dataSourceIn
|
||||
-(void) dataSourceEof:(STKDataSource*)dataSourceIn
|
||||
{
|
||||
if (currentlyReadingEntry.dataSource != dataSourceIn)
|
||||
{
|
||||
@@ -2193,7 +2308,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
|
||||
{
|
||||
OSSpinLockLock(¤tlyPlayingLock);
|
||||
|
||||
QueueEntry* entry = currentlyPlayingEntry;
|
||||
STKQueueEntry* entry = currentlyPlayingEntry;
|
||||
|
||||
if (entry == nil)
|
||||
{
|
||||
|
||||
@@ -36,10 +36,10 @@
|
||||
#import "STKHttpDataSource.h"
|
||||
#import "STKDataSourceWrapper.h"
|
||||
|
||||
@interface STKAutoRecoveringHttpDataSource : DataSourceWrapper
|
||||
@interface STKAutoRecoveringHttpDataSource : STKDataSourceWrapper
|
||||
|
||||
-(id) initWithHttpDataSource:(HttpDataSource*)innerDataSource;
|
||||
-(id) initWithHttpDataSource:(STKHttpDataSource*)innerDataSource;
|
||||
|
||||
@property (readonly) HttpDataSource* innerDataSource;
|
||||
@property (readonly) STKHttpDataSource* innerDataSource;
|
||||
|
||||
@end
|
||||
|
||||
@@ -68,12 +68,17 @@ static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReach
|
||||
|
||||
@implementation STKAutoRecoveringHttpDataSource
|
||||
|
||||
-(HttpDataSource*) innerHttpDataSource
|
||||
-(STKHttpDataSource*) innerHttpDataSource
|
||||
{
|
||||
return (HttpDataSource*)self.innerDataSource;
|
||||
return (STKHttpDataSource*)self.innerDataSource;
|
||||
}
|
||||
|
||||
-(id) initWithHttpDataSource:(HttpDataSource*)innerDataSourceIn
|
||||
-(id) initWithDataSource:(STKDataSource *)innerDataSource
|
||||
{
|
||||
return [self initWithHttpDataSource:(STKHttpDataSource*)innerDataSource];
|
||||
}
|
||||
|
||||
-(id) initWithHttpDataSource:(STKHttpDataSource*)innerDataSourceIn
|
||||
{
|
||||
if (self = [super initWithDataSource:innerDataSourceIn])
|
||||
{
|
||||
@@ -143,6 +148,8 @@ static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReach
|
||||
|
||||
-(void) dealloc
|
||||
{
|
||||
NSLog(@"STKAutoRecoveringHttpDataSource dealloc");
|
||||
|
||||
self.innerDataSource.delegate = nil;
|
||||
|
||||
[self stopNotifier];
|
||||
@@ -165,7 +172,7 @@ static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReach
|
||||
}
|
||||
}
|
||||
|
||||
-(void) dataSourceDataAvailable:(DataSource*)dataSource
|
||||
-(void) dataSourceDataAvailable:(STKDataSource*)dataSource
|
||||
{
|
||||
reconnectAttempts = 0;
|
||||
|
||||
@@ -202,7 +209,7 @@ static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReach
|
||||
}
|
||||
}
|
||||
|
||||
-(void) dataSourceEof:(DataSource*)dataSource
|
||||
-(void) dataSourceEof:(STKDataSource*)dataSource
|
||||
{
|
||||
if ([self position] != [self length])
|
||||
{
|
||||
@@ -214,7 +221,7 @@ static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReach
|
||||
[self.delegate dataSourceEof:self];
|
||||
}
|
||||
|
||||
-(void) dataSourceErrorOccured:(DataSource*)dataSource
|
||||
-(void) dataSourceErrorOccured:(STKDataSource*)dataSource
|
||||
{
|
||||
if (self.innerDataSource.httpStatusCode == 416 /* Range out of bounds */)
|
||||
{
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
@property (readwrite, retain) STKCoreFoundationDataSource* datasource;
|
||||
@end
|
||||
|
||||
@interface STKCoreFoundationDataSource : DataSource
|
||||
@interface STKCoreFoundationDataSource : STKDataSource
|
||||
{
|
||||
@protected
|
||||
CFReadStreamRef stream;
|
||||
|
||||
@@ -35,12 +35,12 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#include <AudioToolbox/AudioToolbox.h>
|
||||
|
||||
@class DataSource;
|
||||
@class STKDataSource;
|
||||
|
||||
@protocol STKDataSourceDelegate<NSObject>
|
||||
-(void) dataSourceDataAvailable:(DataSource*)dataSource;
|
||||
-(void) dataSourceErrorOccured:(DataSource*)dataSource;
|
||||
-(void) dataSourceEof:(DataSource*)dataSource;
|
||||
-(void) dataSourceDataAvailable:(STKDataSource*)dataSource;
|
||||
-(void) dataSourceErrorOccured:(STKDataSource*)dataSource;
|
||||
-(void) dataSourceEof:(STKDataSource*)dataSource;
|
||||
@end
|
||||
|
||||
@protocol AudioDataSource<NSObject>
|
||||
@@ -48,7 +48,7 @@
|
||||
@property (readwrite) long long audioDataOffset;
|
||||
@end
|
||||
|
||||
@interface DataSource : NSObject
|
||||
@interface STKDataSource : NSObject
|
||||
|
||||
@property (readonly) long long position;
|
||||
@property (readonly) long long length;
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
|
||||
#import "STKDataSource.h"
|
||||
|
||||
@implementation DataSource
|
||||
@implementation STKDataSource
|
||||
@synthesize delegate;
|
||||
|
||||
-(long long) length
|
||||
|
||||
@@ -34,10 +34,10 @@
|
||||
|
||||
#import "STKDataSource.h"
|
||||
|
||||
@interface DataSourceWrapper : DataSource<STKDataSourceDelegate>
|
||||
@interface STKDataSourceWrapper : STKDataSource<STKDataSourceDelegate>
|
||||
|
||||
-(id) initWithDataSource:(DataSource*)innerDataSource;
|
||||
-(id) initWithDataSource:(STKDataSource*)innerDataSource;
|
||||
|
||||
@property (readonly) DataSource* innerDataSource;
|
||||
@property (readonly) STKDataSource* innerDataSource;
|
||||
|
||||
@end
|
||||
|
||||
@@ -34,13 +34,13 @@
|
||||
|
||||
#import "STKDataSourceWrapper.h"
|
||||
|
||||
@interface DataSourceWrapper()
|
||||
@property (readwrite) DataSource* innerDataSource;
|
||||
@interface STKDataSourceWrapper()
|
||||
@property (readwrite) STKDataSource* innerDataSource;
|
||||
@end
|
||||
|
||||
@implementation DataSourceWrapper
|
||||
@implementation STKDataSourceWrapper
|
||||
|
||||
-(id) initWithDataSource:(DataSource*)innerDataSourceIn
|
||||
-(id) initWithDataSource:(STKDataSource*)innerDataSourceIn
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
@@ -102,17 +102,17 @@
|
||||
return self.innerDataSource.hasBytesAvailable;
|
||||
}
|
||||
|
||||
-(void) dataSourceDataAvailable:(DataSource*)dataSource
|
||||
-(void) dataSourceDataAvailable:(STKDataSource*)dataSource
|
||||
{
|
||||
[self.delegate dataSourceDataAvailable:self];
|
||||
}
|
||||
|
||||
-(void) dataSourceErrorOccured:(DataSource*)dataSource
|
||||
-(void) dataSourceErrorOccured:(STKDataSource*)dataSource
|
||||
{
|
||||
[self.delegate dataSourceErrorOccured:self];
|
||||
}
|
||||
|
||||
-(void) dataSourceEof:(DataSource*)dataSource
|
||||
-(void) dataSourceEof:(STKDataSource*)dataSource
|
||||
{
|
||||
[self.delegate dataSourceEof:self];
|
||||
}
|
||||
|
||||
@@ -34,21 +34,15 @@
|
||||
|
||||
#import "STKCoreFoundationDataSource.h"
|
||||
|
||||
@interface HttpDataSource : STKCoreFoundationDataSource
|
||||
{
|
||||
@private
|
||||
int seekStart;
|
||||
int relativePosition;
|
||||
long long fileLength;
|
||||
int discontinuous;
|
||||
NSDictionary* httpHeaders;
|
||||
AudioFileTypeID audioFileTypeHint;
|
||||
}
|
||||
typedef NSURL*(^URLProvider)();
|
||||
|
||||
@property (readwrite, retain) NSURL* url;
|
||||
@interface STKHttpDataSource : STKCoreFoundationDataSource
|
||||
|
||||
@property (readonly, retain) NSURL* url;
|
||||
@property (readwrite) UInt32 httpStatusCode;
|
||||
|
||||
+(AudioFileTypeID) audioFileTypeHintFromMimeType:(NSString*)fileExtension;
|
||||
-(id) initWithURL:(NSURL*)url;
|
||||
-(id) initWithURLProvider:(URLProvider)urlProvider;
|
||||
|
||||
@end
|
||||
|
||||
@@ -35,14 +35,30 @@
|
||||
#import "STKHttpDataSource.h"
|
||||
#import "STKLocalFileDataSource.h"
|
||||
|
||||
@interface HttpDataSource()
|
||||
@interface STKHttpDataSource()
|
||||
{
|
||||
@private
|
||||
int seekStart;
|
||||
int relativePosition;
|
||||
long long fileLength;
|
||||
int discontinuous;
|
||||
NSURL* currentUrl;
|
||||
URLProvider urlProvider;
|
||||
NSDictionary* httpHeaders;
|
||||
AudioFileTypeID audioFileTypeHint;
|
||||
}
|
||||
-(void) open;
|
||||
|
||||
@end
|
||||
|
||||
@implementation HttpDataSource
|
||||
@synthesize url;
|
||||
@implementation STKHttpDataSource
|
||||
|
||||
-(id) initWithURL:(NSURL*)urlIn
|
||||
{
|
||||
return [self initWithURLProvider:^NSURL* { return urlIn; }];
|
||||
}
|
||||
|
||||
-(id) initWithURLProvider:(URLProvider)urlProviderIn
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
@@ -50,41 +66,52 @@
|
||||
relativePosition = 0;
|
||||
fileLength = -1;
|
||||
|
||||
self.url = urlIn;
|
||||
self->urlProvider = urlProviderIn;
|
||||
|
||||
[self open];
|
||||
|
||||
audioFileTypeHint = [LocalFileDataSource audioFileTypeHintFromFileExtension:urlIn.pathExtension];
|
||||
audioFileTypeHint = [STKLocalFileDataSource audioFileTypeHintFromFileExtension:self->currentUrl.pathExtension];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
-(void) dealloc
|
||||
{
|
||||
NSLog(@"STKHttpDataSource dealloc");
|
||||
}
|
||||
|
||||
-(NSURL*) url
|
||||
{
|
||||
return self->currentUrl;
|
||||
}
|
||||
|
||||
+(AudioFileTypeID) audioFileTypeHintFromMimeType:(NSString*)mimeType
|
||||
{
|
||||
static dispatch_once_t onceToken;
|
||||
static NSDictionary* fileTypesByMimeType;
|
||||
|
||||
dispatch_once(&onceToken, ^
|
||||
{
|
||||
fileTypesByMimeType =
|
||||
@{
|
||||
@"audio/mp3": @(kAudioFileMP3Type),
|
||||
@"audio/mpg": @(kAudioFileMP3Type),
|
||||
@"audio/mpeg": @(kAudioFileMP3Type),
|
||||
@"audio/wav": @(kAudioFileWAVEType),
|
||||
@"audio/aifc": @(kAudioFileAIFCType),
|
||||
@"audio/aiff": @(kAudioFileAIFFType),
|
||||
@"audio/x-m4a": @(kAudioFileM4AType),
|
||||
@"audio/x-mp4": @(kAudioFileMPEG4Type),
|
||||
@"audio/m4a": @(kAudioFileM4AType),
|
||||
@"audio/mp4": @(kAudioFileMPEG4Type),
|
||||
@"audio/caf": @(kAudioFileCAFType),
|
||||
@"audio/aac": @(kAudioFileAAC_ADTSType),
|
||||
@"audio/ac3": @(kAudioFileAC3Type),
|
||||
@"audio/3gp": @(kAudioFile3GPType)
|
||||
};
|
||||
});
|
||||
{
|
||||
fileTypesByMimeType =
|
||||
@{
|
||||
@"audio/mp3": @(kAudioFileMP3Type),
|
||||
@"audio/mpg": @(kAudioFileMP3Type),
|
||||
@"audio/mpeg": @(kAudioFileMP3Type),
|
||||
@"audio/wav": @(kAudioFileWAVEType),
|
||||
@"audio/aifc": @(kAudioFileAIFCType),
|
||||
@"audio/aiff": @(kAudioFileAIFFType),
|
||||
@"audio/x-m4a": @(kAudioFileM4AType),
|
||||
@"audio/x-mp4": @(kAudioFileMPEG4Type),
|
||||
@"audio/aacp": @(kAudioFileAAC_ADTSType),
|
||||
@"audio/m4a": @(kAudioFileM4AType),
|
||||
@"audio/mp4": @(kAudioFileMPEG4Type),
|
||||
@"audio/caf": @(kAudioFileCAFType),
|
||||
@"audio/aac": @(kAudioFileAAC_ADTSType),
|
||||
@"audio/ac3": @(kAudioFileAC3Type),
|
||||
@"audio/3gp": @(kAudioFile3GPType)
|
||||
};
|
||||
});
|
||||
|
||||
NSNumber* number = [fileTypesByMimeType objectForKey:mimeType];
|
||||
|
||||
@@ -121,7 +148,7 @@
|
||||
}
|
||||
|
||||
NSString* contentType = [httpHeaders objectForKey:@"Content-Type"];
|
||||
AudioFileTypeID typeIdFromMimeType = [HttpDataSource audioFileTypeHintFromMimeType:contentType];
|
||||
AudioFileTypeID typeIdFromMimeType = [STKHttpDataSource audioFileTypeHintFromMimeType:contentType];
|
||||
|
||||
if (typeIdFromMimeType != 0)
|
||||
{
|
||||
@@ -191,7 +218,9 @@
|
||||
|
||||
-(void) open
|
||||
{
|
||||
CFHTTPMessageRef message = CFHTTPMessageCreateRequest(NULL, (CFStringRef)@"GET", (__bridge CFURLRef)self.url, kCFHTTPVersion1_1);
|
||||
self->currentUrl = urlProvider();
|
||||
|
||||
CFHTTPMessageRef message = CFHTTPMessageCreateRequest(NULL, (CFStringRef)@"GET", (__bridge CFURLRef)self->currentUrl, kCFHTTPVersion1_1);
|
||||
|
||||
if (seekStart > 0)
|
||||
{
|
||||
@@ -206,6 +235,8 @@
|
||||
{
|
||||
CFRelease(message);
|
||||
|
||||
[self errorOccured];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -213,6 +244,8 @@
|
||||
{
|
||||
CFRelease(message);
|
||||
|
||||
[self errorOccured];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -224,7 +257,7 @@
|
||||
|
||||
// SSL support
|
||||
|
||||
if ([url.scheme caseInsensitiveCompare:@"https"] == NSOrderedSame)
|
||||
if ([self->currentUrl.scheme caseInsensitiveCompare:@"https"] == NSOrderedSame)
|
||||
{
|
||||
NSDictionary* sslSettings = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
(NSString*)kCFStreamSocketSecurityLevelNegotiatedSSL, kCFStreamSSLLevel,
|
||||
@@ -245,6 +278,8 @@
|
||||
CFRelease(stream);
|
||||
CFRelease(message);
|
||||
|
||||
[self errorOccured];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,18 +34,10 @@
|
||||
|
||||
#import "STKCoreFoundationDataSource.h"
|
||||
|
||||
@interface LocalFileDataSource : STKCoreFoundationDataSource
|
||||
{
|
||||
@private
|
||||
long long position;
|
||||
long long length;
|
||||
AudioFileTypeID audioFileTypeHint;
|
||||
}
|
||||
@interface STKLocalFileDataSource : STKCoreFoundationDataSource
|
||||
|
||||
+(AudioFileTypeID) audioFileTypeHintFromFileExtension:(NSString*)fileExtension;
|
||||
|
||||
@property (readonly, copy) NSString* filePath;
|
||||
|
||||
-(id) initWithFilePath:(NSString*)filePath;
|
||||
|
||||
@end
|
||||
|
||||
@@ -34,13 +34,17 @@
|
||||
|
||||
#import "STKLocalFileDataSource.h"
|
||||
|
||||
@interface LocalFileDataSource()
|
||||
@interface STKLocalFileDataSource()
|
||||
{
|
||||
long long position;
|
||||
long long length;
|
||||
AudioFileTypeID audioFileTypeHint;
|
||||
}
|
||||
@property (readwrite, copy) NSString* filePath;
|
||||
|
||||
-(void) open;
|
||||
@end
|
||||
|
||||
@implementation LocalFileDataSource
|
||||
@implementation STKLocalFileDataSource
|
||||
@synthesize filePath;
|
||||
|
||||
-(id) initWithFilePath:(NSString*)filePathIn
|
||||
@@ -51,7 +55,7 @@
|
||||
|
||||
[self open];
|
||||
|
||||
audioFileTypeHint = [LocalFileDataSource audioFileTypeHintFromFileExtension:filePathIn.pathExtension];
|
||||
audioFileTypeHint = [STKLocalFileDataSource audioFileTypeHintFromFileExtension:filePathIn.pathExtension];
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -111,8 +115,6 @@
|
||||
|
||||
-(void) open
|
||||
{
|
||||
NSURL* url = [[NSURL alloc] initFileURLWithPath:self.filePath];
|
||||
|
||||
if (stream)
|
||||
{
|
||||
CFReadStreamClose(stream);
|
||||
@@ -121,16 +123,13 @@
|
||||
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)
|
||||
{
|
||||
@@ -141,7 +140,6 @@
|
||||
}
|
||||
|
||||
NSNumber* number = [attributes objectForKey:@"NSFileSize"];
|
||||
|
||||
|
||||
if (number)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user