Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c68384ec50 | |||
| 756504f33b | |||
| 894dce8761 | |||
| c3a35fcdf3 | |||
| ce3f938309 | |||
| 429c156d8b | |||
| 0eb7687a63 | |||
| 9b952de734 |
@@ -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 */,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"];
|
||||
|
||||
|
||||
```
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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(¤tAudioStreamBasicDescription, ¤tlyReadingEntry->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(¤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];
|
||||
@@ -2193,7 +2308,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
|
||||
{
|
||||
OSSpinLockLock(¤tlyPlayingLock);
|
||||
|
||||
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];
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user