Compare commits

...

9 Commits

Author SHA1 Message Date
Thong Nguyen c9ba30b692 Removed iOS7 specific calls. Changed default target to iOS 4.3 2014-01-21 15:48:12 +00:00
Thong Nguyen c68384ec50 Added play:(NSString*) and playWithURL:(NSURL*) methods 2014-01-21 09:41:14 +00:00
Thong Nguyen 756504f33b Renamed QueueEntry to STKQueueEntry 2014-01-21 00:08:04 +00:00
Thong Nguyen 894dce8761 Updated README 2014-01-20 23:57:45 +00:00
Thong Nguyen c3a35fcdf3 Updated README 2014-01-20 23:57:18 +00:00
Thong Nguyen ce3f938309 Added playWithDataSource method. Updated README 2014-01-20 23:56:32 +00:00
Thong Nguyen 429c156d8b STKHttpDataSource now raises errors when failing to allocate resources for opening a stream 2014-01-20 23:47:55 +00:00
Thong Nguyen 0eb7687a63 Prevent crash when reading unsupported types and raise didEncounterError delegate callback 2014-01-20 23:22:41 +00:00
Thong Nguyen 9b952de734 Updated STKHttpDataSource to support dynamically generated URLs with each 'open' or 'seek') 2014-01-20 23:14:59 +00:00
11 changed files with 240 additions and 146 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 */; };
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,11 +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";
IPHONEOS_DEPLOYMENT_TARGET = 6.0;
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = app;
};
@@ -410,11 +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";
IPHONEOS_DEPLOYMENT_TARGET = 6.0;
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = app;
};
+4 -1
View File
@@ -9,6 +9,7 @@
#import "AppDelegate.h"
#import "STKAudioPlayer.h"
#import "AudioPlayerView.h"
#import "STKAutoRecoveringHttpDataSource.h"
#import "SampleQueueId.h"
#import <AVFoundation/AVFoundation.h>
@@ -48,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
+3 -6
View File
@@ -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,18 +20,15 @@ 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"];
```
@@ -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 */
};
+8 -66
View File
@@ -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;
@@ -195,7 +135,9 @@ AudioQueueBufferRefLookupEntry;
-(id) init;
-(id) initWithNumberOfAudioQueueBuffers:(int)numberOfAudioQueueBuffers andReadBufferSize:(int)readBufferSizeIn;
-(STKDataSource*) dataSourceFromURL:(NSURL*)url;
-(void) play:(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;
+150 -35
View File
@@ -97,7 +97,7 @@
@end
@interface QueueEntry : NSObject
@interface STKQueueEntry : NSObject
{
@public
BOOL parsedHeader;
@@ -127,7 +127,7 @@
@end
@implementation QueueEntry
@implementation STKQueueEntry
@synthesize dataSource, queueItemId, bufferIndex;
-(id) initWithDataSource:(STKDataSource*)dataSourceIn andQueueItemId:(NSObject*)queueItemIdIn
@@ -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;
@@ -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,21 +609,34 @@ 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) 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];
@@ -584,7 +664,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
{
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(&currentAudioStreamBasicDescription, &currentlyReadingEntry->audioStreamBasicDescription, sizeof(currentAudioStreamBasicDescription)) != 0)
{
if (currentlyReadingEntry == currentlyPlayingEntry)
{
[self createAudioQueue];
if (audioQueue == nil)
{
return;
}
}
else
{
@@ -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(&currentlyPlayingLock);
QueueEntry* entry = currentlyPlayingEntry;
STKQueueEntry* entry = currentlyPlayingEntry;
if (entry == nil)
{
@@ -1275,7 +1387,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
OSSpinLockLock(&currentlyPlayingLock);
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(&currentlyPlayingLock);
QueueEntry* currentEntry = currentlyReadingEntry;
STKQueueEntry* currentEntry = currentlyReadingEntry;
OSSpinLockUnlock(&currentlyPlayingLock);
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];
@@ -2193,7 +2308,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
{
OSSpinLockLock(&currentlyPlayingLock);
QueueEntry* entry = currentlyPlayingEntry;
STKQueueEntry* entry = currentlyPlayingEntry;
if (entry == nil)
{
@@ -73,6 +73,11 @@ static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReach
return (STKHttpDataSource*)self.innerDataSource;
}
-(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];
+5 -11
View File
@@ -34,21 +34,15 @@
#import "STKCoreFoundationDataSource.h"
@interface STKHttpDataSource : 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
+39 -5
View File
@@ -36,13 +36,29 @@
#import "STKLocalFileDataSource.h"
@interface STKHttpDataSource()
{
@private
int seekStart;
int relativePosition;
long long fileLength;
int discontinuous;
NSURL* currentUrl;
URLProvider urlProvider;
NSDictionary* httpHeaders;
AudioFileTypeID audioFileTypeHint;
}
-(void) open;
@end
@implementation STKHttpDataSource
@synthesize url;
-(id) initWithURL:(NSURL*)urlIn
{
return [self initWithURLProvider:^NSURL* { return urlIn; }];
}
-(id) initWithURLProvider:(URLProvider)urlProviderIn
{
if (self = [super init])
{
@@ -50,16 +66,26 @@
relativePosition = 0;
fileLength = -1;
self.url = urlIn;
self->urlProvider = urlProviderIn;
[self open];
audioFileTypeHint = [STKLocalFileDataSource 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;
@@ -192,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)
{
@@ -207,6 +235,8 @@
{
CFRelease(message);
[self errorOccured];
return;
}
@@ -214,6 +244,8 @@
{
CFRelease(message);
[self errorOccured];
return;
}
@@ -225,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,
@@ -246,6 +278,8 @@
CFRelease(stream);
CFRelease(message);
[self errorOccured];
return;
}
@@ -35,17 +35,9 @@
#import "STKCoreFoundationDataSource.h"
@interface STKLocalFileDataSource : STKCoreFoundationDataSource
{
@private
long long position;
long long length;
AudioFileTypeID audioFileTypeHint;
}
+(AudioFileTypeID) audioFileTypeHintFromFileExtension:(NSString*)fileExtension;
@property (readonly, copy) NSString* filePath;
-(id) initWithFilePath:(NSString*)filePath;
@end
@@ -35,8 +35,12 @@
#import "STKLocalFileDataSource.h"
@interface STKLocalFileDataSource()
{
long long position;
long long length;
AudioFileTypeID audioFileTypeHint;
}
@property (readwrite, copy) NSString* filePath;
-(void) open;
@end
@@ -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)
{