Compare commits

..

1 Commits

Author SHA1 Message Date
Thong Nguyen bf13ba318f Buffering branch 2014-03-13 18:15:27 +00:00
15 changed files with 471 additions and 77 deletions
@@ -365,7 +365,7 @@
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
"$(SRCROOT)/../StreamingKit/StreamingKit",
);
IPHONEOS_DEPLOYMENT_TARGET = 4.3;
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
@@ -403,7 +403,7 @@
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
"$(SRCROOT)/../StreamingKit/StreamingKit",
);
IPHONEOS_DEPLOYMENT_TARGET = 4.3;
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
+2 -3
View File
@@ -38,7 +38,6 @@
audioPlayer = [[STKAudioPlayer alloc] initWithOptions:(STKAudioPlayerOptions){ .flushQueueOnSeek = YES, .enableVolumeMixer = NO, .equalizerBandFrequencies = {50, 100, 200, 400, 800, 1600, 2600, 16000} }];
audioPlayer.meteringEnabled = YES;
audioPlayer.volume = 1;
AudioPlayerView* audioPlayerView = [[AudioPlayerView alloc] initWithFrame:self.window.bounds andAudioPlayer:audioPlayer];
@@ -61,7 +60,7 @@
-(void) audioPlayerViewPlayFromHTTPSelected:(AudioPlayerView*)audioPlayerView
{
NSURL* url = [NSURL URLWithString:@"file:///Users/tum/Temp/airplane-cut.aac"];
NSURL* url = [NSURL URLWithString:@"http://fs.bloom.fm/oss/audiosamples/sample.mp3"];
STKDataSource* dataSource = [STKAudioPlayer dataSourceFromURL:url];
@@ -90,7 +89,7 @@
-(void) audioPlayerViewQueuePcmWaveFileSelected:(AudioPlayerView*)audioPlayerView
{
NSURL* url = [NSURL URLWithString:@"http://www.abstractpath.com/files/audiosamples/perfectly.wav"];
NSURL* url = [NSURL URLWithString:@"http://fs.bloom.fm/oss/audiosamples/perfectly.wav"];
STKDataSource* dataSource = [STKAudioPlayer dataSourceFromURL:url];
+1 -1
View File
@@ -58,7 +58,7 @@
-(void) playFromHTTP
{
[audioPlayer play:@"http://www.abstractpath.com/files/audiosamples/sample.mp3"];
[audioPlayer play:@"http://fs.bloom.fm/oss/audiosamples/sample.mp3"];
}
-(void) tick:(NSTimer*)timer
+4 -4
View File
@@ -2,7 +2,7 @@
StreamingKit (formally Audjustable) is an audio playback and streaming library for iOS and Mac OSX. StreamingKit uses CoreAudio to decompress and playback audio (using hardware or software codecs) whilst providing a clean and simple object-oriented API.
The primary motivation of this project was to decouple the input data sources from the actual player logic in order to allow advanced customizable input handling such as HTTP progressive download based streaming, encryption/decryption, auto-recovery, dynamic-buffering. StreamingKit is the only streaming and playback library that supports dead-easy [gapless playback](https://github.com/tumtumtum/StreamingKit/wiki/Gapless-playback) between audio files of differing formats.
The primary motivation of this project was to decouple the input data sources from the actual player logic in order to allow advanced customizable input handling such as HTTP streaming, encryption/decryption, auto-recovery, dynamic-buffering. StreamingKit is the only streaming and playback library that supports dead-easy [gapless playback](https://github.com/tumtumtum/StreamingKit/wiki/Gapless-playback) between audio files of differing formats.
## Main Features
@@ -34,7 +34,7 @@ There are two main classes. The `STKDataSource` class which is the abstract bas
```objective-c
STKAudioPlayer* audioPlayer = [[STKAudioPlayer alloc] init];
[audioPlayer play:@"http://www.abstractpath.com/files/audiosamples/sample.mp3"];
[audioPlayer play:@"http://fs.bloom.fm/oss/audiosamples/sample.mp3"];
```
### Gapless playback
@@ -42,8 +42,8 @@ STKAudioPlayer* audioPlayer = [[STKAudioPlayer alloc] init];
```objective-c
STKAudioPlayer* audioPlayer = [[STKAudioPlayer alloc] init];
[audioPlayer queue:@"http://www.abstractpath.com/files/audiosamples/sample.mp3"];
[audioPlayer queue:@"http://www.abstractpath.com/files/audiosamples/airplane.aac"];
[audioPlayer queue:@"http://fs.bloom.fm/oss/audiosamples/sample.mp3"];
[audioPlayer queue:@"http://fs.bloom.fm/oss/audiosamples/airplane.aac"];
```
+1 -1
View File
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "StreamingKit"
s.version = "0.1.23"
s.version = "0.1.19"
s.summary = "A fast and extensible audio streamer for iOS and OSX with support for gapless playback and custom (non-HTTP) sources."
s.homepage = "https://github.com/tumtumtum/StreamingKit/"
s.license = 'MIT'
@@ -7,6 +7,8 @@
objects = {
/* Begin PBXBuildFile section */
A1682FA318B3903900F29FEC /* STKBufferingDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = A1682FA218B3903900F29FEC /* STKBufferingDataSource.m */; };
A168C6F118BB67DC003D170D /* STKBufferChunk.m in Sources */ = {isa = PBXBuildFile; fileRef = A168C6F018BB67DC003D170D /* STKBufferChunk.m */; };
A1A4996B189E744400E2A2E2 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1A4996A189E744400E2A2E2 /* Cocoa.framework */; };
A1A49975189E744500E2A2E2 /* StreamingKitMac.m in Sources */ = {isa = PBXBuildFile; fileRef = A1A49974189E744500E2A2E2 /* StreamingKitMac.m */; };
A1A4997B189E744500E2A2E2 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1E7C4D9188D57F60010896F /* XCTest.framework */; };
@@ -83,6 +85,10 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
A1682FA118B3903900F29FEC /* STKBufferingDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STKBufferingDataSource.h; sourceTree = "<group>"; };
A1682FA218B3903900F29FEC /* STKBufferingDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STKBufferingDataSource.m; sourceTree = "<group>"; };
A168C6EF18BB67DC003D170D /* STKBufferChunk.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STKBufferChunk.h; sourceTree = "<group>"; };
A168C6F018BB67DC003D170D /* STKBufferChunk.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STKBufferChunk.m; sourceTree = "<group>"; };
A1A49969189E744400E2A2E2 /* libStreamingKitMac.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libStreamingKitMac.a; sourceTree = BUILT_PRODUCTS_DIR; };
A1A4996A189E744400E2A2E2 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = Library/Frameworks/Cocoa.framework; sourceTree = DEVELOPER_DIR; };
A1A4996D189E744500E2A2E2 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
@@ -266,6 +272,10 @@
A1E7C4F2188D5E550010896F /* STKAudioPlayer.m */,
A1E7C4F3188D5E550010896F /* STKAutoRecoveringHTTPDataSource.h */,
A1E7C4F4188D5E550010896F /* STKAutoRecoveringHTTPDataSource.m */,
A168C6EF18BB67DC003D170D /* STKBufferChunk.h */,
A168C6F018BB67DC003D170D /* STKBufferChunk.m */,
A1682FA118B3903900F29FEC /* STKBufferingDataSource.h */,
A1682FA218B3903900F29FEC /* STKBufferingDataSource.m */,
A1E7C4F5188D5E550010896F /* STKCoreFoundationDataSource.h */,
A1E7C4F6188D5E550010896F /* STKCoreFoundationDataSource.m */,
A1E7C4F7188D5E550010896F /* STKDataSource.h */,
@@ -502,7 +512,9 @@
A1BF65D2189A6582004DD08C /* STKQueueEntry.m in Sources */,
A1E7C504188D5E550010896F /* STKHTTPDataSource.m in Sources */,
A1E7C503188D5E550010896F /* STKDataSourceWrapper.m in Sources */,
A1682FA318B3903900F29FEC /* STKBufferingDataSource.m in Sources */,
A1E7C502188D5E550010896F /* STKDataSource.m in Sources */,
A168C6F118BB67DC003D170D /* STKBufferChunk.m in Sources */,
A1BF65D5189A65C6004DD08C /* NSMutableArray+STKAudioPlayer.m in Sources */,
A1E7C500188D5E550010896F /* STKAutoRecoveringHTTPDataSource.m in Sources */,
);
@@ -662,7 +674,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 4.3;
IPHONEOS_DEPLOYMENT_TARGET = 6.0;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
};
@@ -693,7 +705,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 4.3;
IPHONEOS_DEPLOYMENT_TARGET = 6.0;
SDKROOT = iphoneos;
VALIDATE_PRODUCT = YES;
};
+19 -35
View File
@@ -42,10 +42,6 @@
#import "libkern/OSAtomic.h"
#import <float.h>
#ifndef DBL_MAX
#define DBL_MAX 1.7976931348623157e+308
#endif
#pragma mark Defines
#define kOutputBus 0
@@ -530,12 +526,7 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
{
currentlyPlayingEntry.dataSource.delegate = nil;
[currentlyReadingEntry.dataSource unregisterForEvents];
OSSpinLockLock(&currentEntryReferencesLock);
currentlyPlayingEntry = nil;
OSSpinLockUnlock(&currentEntryReferencesLock);
}
[self stopAudioUnitWithReason:STKAudioPlayerStopReasonDisposed];
@@ -805,11 +796,7 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
pthread_mutex_lock(&playerMutex);
if (entryToUpdate->audioStreamBasicDescription.mFormatID == 0)
{
entryToUpdate->audioStreamBasicDescription = newBasicDescription;
}
entryToUpdate->audioStreamBasicDescription = newBasicDescription;
entryToUpdate->sampleRate = entryToUpdate->audioStreamBasicDescription.mSampleRate;
entryToUpdate->packetDuration = entryToUpdate->audioStreamBasicDescription.mFramesPerPacket / entryToUpdate->sampleRate;
@@ -890,8 +877,13 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
if (pasbd.mFormatID == kAudioFormatMPEG4AAC_HE || pasbd.mFormatID == kAudioFormatMPEG4AAC_HE_V2)
{
//
// We've found HE-AAC, remember this to tell the audio queue
// when we construct it.
//
#if !TARGET_IPHONE_SIMULATOR
currentlyReadingEntry->audioStreamBasicDescription = pasbd;
#endif
break;
}
}
@@ -932,15 +924,20 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
}
OSSpinLockLock(&currentEntryReferencesLock);
STKQueueEntry* entry = currentlyPlayingEntry;
OSSpinLockUnlock(&currentEntryReferencesLock);
if (entry == nil)
{
return 0;
OSSpinLockUnlock(&currentEntryReferencesLock);
return 0;
}
double retval = [entry duration];
OSSpinLockUnlock(&currentEntryReferencesLock);
double progress = [self progress];
if (retval < progress && retval > 0)
@@ -963,9 +960,7 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
return 0;
}
OSSpinLockLock(&currentEntryReferencesLock);
STKQueueEntry* entry = currentlyPlayingEntry;
OSSpinLockUnlock(&currentEntryReferencesLock);
if (entry == nil)
{
@@ -1352,6 +1347,7 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
}
}
pthread_mutex_unlock(&playerMutex);
return YES;
}
@@ -1402,8 +1398,6 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
playbackThreadRunLoop = nil;
[self destroyAudioResources];
[threadFinishedCondLock lock];
[threadFinishedCondLock unlockWithCondition:1];
}
@@ -2536,15 +2530,11 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags*
{
STKAudioPlayer* audioPlayer = (__bridge STKAudioPlayer*)inRefCon;
OSSpinLockLock(&audioPlayer->currentEntryReferencesLock);
STKQueueEntry* entry = audioPlayer->currentlyPlayingEntry;
STKQueueEntry* currentlyReadingEntry = audioPlayer->currentlyReadingEntry;
OSSpinLockUnlock(&audioPlayer->currentEntryReferencesLock);
OSSpinLockLock(&audioPlayer->pcmBufferSpinLock);
BOOL waitForBuffer = NO;
BOOL muted = audioPlayer->muted;
STKQueueEntry* entry = audioPlayer->currentlyPlayingEntry;
AudioBuffer* audioBuffer = audioPlayer->pcmAudioBuffer;
UInt32 frameSizeInBytes = audioPlayer->pcmBufferFrameSizeInBytes;
UInt32 used = audioPlayer->pcmBufferUsedFrameCount;
@@ -2562,10 +2552,10 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags*
if (entry->lastFrameQueued >= 0)
{
framesRequiredToStartPlaying = MIN(framesRequiredToStartPlaying, entry->lastFrameQueued);
framesRequiredToStartPlaying = MIN(framesRequiredToStartPlaying, audioPlayer->currentlyPlayingEntry->lastFrameQueued);
}
if (entry && currentlyReadingEntry == entry
if (entry && audioPlayer->currentlyReadingEntry == entry
&& entry->framesQueued < framesRequiredToStartPlaying)
{
waitForBuffer = YES;
@@ -2775,19 +2765,13 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags*
{
pthread_mutex_lock(&audioPlayer->playerMutex);
OSSpinLockLock(&audioPlayer->currentEntryReferencesLock);
STKQueueEntry* currentlyPlayingEntry = audioPlayer->currentlyPlayingEntry;
OSSpinLockUnlock(&audioPlayer->currentEntryReferencesLock);
if (lastFramePlayed && entry == currentlyPlayingEntry)
if (lastFramePlayed && entry == audioPlayer->currentlyPlayingEntry)
{
[audioPlayer audioQueueFinishedPlaying:entry];
while (extraFramesPlayedNotAssigned > 0)
{
OSSpinLockLock(&audioPlayer->currentEntryReferencesLock);
STKQueueEntry* newEntry = audioPlayer->currentlyPlayingEntry;
OSSpinLockUnlock(&audioPlayer->currentEntryReferencesLock);
if (newEntry != nil)
{
@@ -0,0 +1,25 @@
//
// STKBufferChunk.h
// StreamingKit
//
// Created by Thong Nguyen on 24/02/2014.
// Copyright (c) 2014 Thong Nguyen. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface STKBufferChunk : NSObject
{
@public
UInt32 index;
UInt32 size;
UInt32 position;
UInt8* buffer;
}
@property (readonly) UInt32 absoluteStart;
@property (readonly) UInt32 absolutePosition;
-(id) initWithBufferSize:(UInt32)sizeIn;
@end
@@ -0,0 +1,40 @@
//
// STKBufferChunk.m
// StreamingKit
//
// Created by Thong Nguyen on 24/02/2014.
// Copyright (c) 2014 Thong Nguyen. All rights reserved.
//
#import "STKBufferChunk.h"
@implementation STKBufferChunk
-(id) initWithBufferSize:(UInt32)sizeIn
{
if (self = [super init])
{
self->size = sizeIn;
self->buffer = calloc(sizeof(UInt8), sizeIn);
}
return self;
}
-(void) dealloc
{
free(self->buffer);
}
-(UInt32) absoluteStart
{
return self->index * self->size;
}
-(UInt32) absolutePosition
{
return self.absoluteStart + self->position;
}
@end
@@ -0,0 +1,44 @@
/**********************************************************************************
STKBufferingDataSource.h
Created by Thong Nguyen on 16/10/2012.
https://github.com/tumtumtum/audjustable
Copyright (c) 2012-2014 Thong Nguyen (tumtumtum@gmail.com). All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. All advertising materials mentioning features or use of this software
must display the following acknowledgement:
This product includes software developed by Thong Nguyen (tumtumtum@gmail.com)
4. Neither the name of Thong Nguyen nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY Thong Nguyen ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THONG NGUYEN BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**********************************************************************************/
#import "STKDataSource.h"
@interface STKBufferingDataSource : STKDataSource
@property (readonly) SInt64 position;
@property (readonly) SInt64 length;
-(id) initWithDataSource:(STKDataSource*)dataSourceIn withMaxSize:(int)maxSizeIn;
@end
@@ -0,0 +1,319 @@
/**********************************************************************************
STKBufferingDataSource.m
Created by Thong Nguyen on 16/10/2012.
https://github.com/tumtumtum/audjustable
Copyright (c) 2012-2014 Thong Nguyen (tumtumtum@gmail.com). All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. All advertising materials mentioning features or use of this software
must display the following acknowledgement:
This product includes software developed by Thong Nguyen (tumtumtum@gmail.com)
4. Neither the name of Thong Nguyen nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY Thong Nguyen ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THONG NGUYEN BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**********************************************************************************/
#import "STKBufferingDataSource.h"
#import "STKBufferChunk.h"
#import <pthread.h>
#define STK_BUFFER_CHUNK_SIZE (128 * 1024)
@interface STKBufferingDataSource()
{
@private
NSRunLoop* runLoop;
SInt32 maxSize;
UInt32 chunkSize;
UInt32 chunkCount;
SInt64 position;
pthread_mutex_t mutex;
pthread_cond_t condition;
STKBufferChunk* __strong * bufferChunks;
STKDataSource* dataSource;
}
@end
@interface STKBufferingDataSourceThread : NSThread
{
@private
NSRunLoop* runLoop;
NSConditionLock* threadStartedLock;
}
@end
@implementation STKBufferingDataSourceThread
-(id) init
{
if (self = [super init])
{
threadStartedLock = [[NSConditionLock alloc] initWithCondition:0];
}
return self;
}
-(NSRunLoop*) runLoop
{
[threadStartedLock lockWhenCondition:1];
[threadStartedLock unlockWithCondition:0];
return self->runLoop;
}
-(void) main
{
runLoop = [NSRunLoop currentRunLoop];
[threadStartedLock lockWhenCondition:0];
[threadStartedLock unlockWithCondition:1];
[runLoop addPort:[NSPort port] forMode:NSDefaultRunLoopMode];
while (true)
{
NSDate* date = [[NSDate alloc] initWithTimeIntervalSinceNow:10];
[runLoop runMode:NSDefaultRunLoopMode beforeDate:date];
}
}
@end
static STKBufferingDataSourceThread* thread;
@implementation STKBufferingDataSource
+(void) initialize
{
thread = [[STKBufferingDataSourceThread alloc] init];
[thread start];
}
-(id) initWithDataSource:(STKDataSource*)dataSourceIn withMaxSize:(int)maxSizeIn
{
if (self = [super init])
{
self->maxSize = maxSizeIn;
self->dataSource = dataSourceIn;
self->chunkSize = STK_BUFFER_CHUNK_SIZE;
self->dataSource.delegate = self.delegate;
[self->dataSource registerForEvents:[thread runLoop]];
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&self->mutex, &attr);
pthread_cond_init(&self->condition, NULL);
}
return self;
}
-(void) dealloc
{
self->dataSource.delegate = nil;
for (int i = 0; i < self->chunkCount; i++)
{
self->bufferChunks[i] = nil;
}
free(self->bufferChunks);
pthread_mutex_destroy(&self->mutex);
pthread_cond_destroy(&self->condition);
}
-(void) createBuffer
{
if (self->bufferChunks == nil)
{
int length = (int)MIN(self.length == 0? 1024 * 1024 : self.length, self->maxSize);
self->chunkCount = (int)((length / self->chunkSize) + 1);
self->bufferChunks = (__strong STKBufferChunk**)calloc(sizeof(STKBufferChunk*), self->chunkCount);
}
}
-(STKBufferChunk*) chunkForPosition:(SInt64)positionIn createIfNotExist:(BOOL)createIfNotExist
{
int chunkIndex = (int)(positionIn / chunkCount);
if (self->bufferChunks[chunkIndex] == nil && createIfNotExist)
{
self->bufferChunks[chunkIndex] = [[STKBufferChunk alloc] initWithBufferSize:STK_BUFFER_CHUNK_SIZE];
}
return self->bufferChunks[chunkIndex];
}
-(SInt64) length
{
return self->dataSource.length;
}
-(void) seekToOffset:(SInt64)offset
{
pthread_mutex_lock(&mutex);
[self seekToNextGap];
pthread_mutex_unlock(&mutex);
}
-(BOOL) hasBytesAvailable
{
return NO;
}
-(int) readIntoBuffer:(UInt8*)bufferIn withSize:(int)size
{
return 0;
}
-(void) invokeBlockOnEventsRunLoop:(void(^)())block
{
if (!runLoop)
{
return;
}
block = [block copy];
CFRunLoopPerformBlock(runLoop.getCFRunLoop, NSRunLoopCommonModes, ^
{
if ([self hasBytesAvailable])
{
block();
}
});
CFRunLoopWakeUp(runLoop.getCFRunLoop);
}
-(BOOL) registerForEvents:(NSRunLoop*)runLoopIn
{
runLoop = runLoopIn;
[dataSource registerForEvents:[thread runLoop]];
return YES;
}
-(void) unregisterForEvents
{
runLoop = nil;
[dataSource unregisterForEvents];
}
-(void) close
{
[dataSource unregisterForEvents];
[dataSource close];
}
-(void) seekToNextGap
{
int startChunkIndex = (int)(self->position / chunkCount);
for (int i = 0; i < self->chunkCount; i++)
{
int chunkIndex = (i + startChunkIndex) % self->chunkCount;
STKBufferChunk* chunk = self->bufferChunks[chunkIndex];
if (chunk == nil)
{
chunk = [[STKBufferChunk alloc] initWithBufferSize:STK_BUFFER_CHUNK_SIZE];
chunk->index = chunkIndex;
self->bufferChunks[chunkIndex] = chunk;
}
if (chunk->position < chunk->size)
{
[dataSource seekToOffset:(self->chunkSize * chunk->index) + chunk->position];
}
}
}
-(void) dataSourceDataAvailable:(STKDataSource*)dataSourceIn
{
if (![dataSourceIn hasBytesAvailable])
{
return;
}
pthread_mutex_lock(&mutex);
if (self->bufferChunks == nil)
{
[self createBuffer];
}
SInt64 sourcePosition = dataSourceIn.position;
STKBufferChunk* chunk = [self chunkForPosition:sourcePosition createIfNotExist:YES];
if (chunk->position >= chunk->size)
{
[self seekToNextGap];
return;
}
int offset = dataSourceIn.position % self->chunkSize;
if (offset >= chunk->position)
{
[self seekToNextGap];
return;
}
int bytesToRead = self->chunkSize - offset;
int bytesRead = [dataSourceIn readIntoBuffer:(chunk->buffer + offset) withSize:bytesToRead];
chunk->position = offset + bytesRead;
pthread_mutex_unlock(&mutex);
}
-(void) dataSourceErrorOccured:(STKDataSource*)dataSourceIn
{
[self.delegate dataSourceErrorOccured:self];
}
-(void) dataSourceEof:(STKDataSource*)dataSourceIn
{
}
@end
@@ -47,7 +47,6 @@ typedef void(^STKAsyncURLProvider)(STKHTTPDataSource* dataSource, BOOL forSeek,
+(AudioFileTypeID) audioFileTypeHintFromMimeType:(NSString*)fileExtension;
-(id) initWithURL:(NSURL*)url;
-(id) initWithURL:(NSURL *)url httpRequestHeaders:(NSDictionary *)httpRequestHeaders;
-(id) initWithURLProvider:(STKURLProvider)urlProvider;
-(id) initWithAsyncURLProvider:(STKAsyncURLProvider)asyncUrlProvider;
-(NSRunLoop*) eventsRunLoop;
@@ -49,7 +49,6 @@
STKAsyncURLProvider asyncUrlProvider;
NSDictionary* httpHeaders;
AudioFileTypeID audioFileTypeHint;
NSDictionary* requestHeaders;
}
-(void) open;
@@ -62,13 +61,6 @@
return [self initWithURLProvider:^NSURL* { return urlIn; }];
}
-(id) initWithURL:(NSURL *)urlIn httpRequestHeaders:(NSDictionary *)httpRequestHeaders
{
self = [self initWithURLProvider:^NSURL* { return urlIn; }];
self->requestHeaders = httpRequestHeaders;
return self;
}
-(id) initWithURLProvider:(STKURLProvider)urlProviderIn
{
urlProviderIn = [urlProviderIn copy];
@@ -307,12 +299,6 @@
discontinuous = YES;
}
for (NSString* key in self->requestHeaders)
{
NSString* value = [self->requestHeaders objectForKey:key];
CFHTTPMessageSetHeaderFieldValue(message, (__bridge CFStringRef)key, (__bridge CFStringRef)value);
}
stream = CFReadStreamCreateForHTTPRequest(NULL, message);
if (stream == nil)
@@ -200,18 +200,6 @@
[self open];
}
if (stream == 0)
{
CFRunLoopPerformBlock(eventsRunLoop.getCFRunLoop, NSRunLoopCommonModes, ^
{
[self errorOccured];
});
CFRunLoopWakeUp(eventsRunLoop.getCFRunLoop);
return;
}
if (CFReadStreamSetProperty(stream, kCFStreamPropertyFileCurrentOffset, (__bridge CFTypeRef)[NSNumber numberWithLongLong:offset]) != TRUE)
{
position = 0;
@@ -18,8 +18,6 @@
{
if (self = [super init])
{
self->spinLock = OS_SPINLOCK_INIT;
self.dataSource = dataSourceIn;
self.queueItemId = queueItemIdIn;
self->lastFrameQueued = -1;