Compare commits
49 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a615419404 | |||
| e43a4613f8 | |||
| ca928dfe1e | |||
| d7d583c3ba | |||
| 7540045361 | |||
| 972ae0e15b | |||
| e3ed6c6dee | |||
| 3398e8c64e | |||
| f99201f54d | |||
| 44b9e7d2d1 | |||
| 7aae2bcb6b | |||
| 5a8068b859 | |||
| 728fc5bb21 | |||
| c4053c964e | |||
| c31df15a43 | |||
| 923baf5b89 | |||
| 9199785202 | |||
| 0618027252 | |||
| 188f880f5a | |||
| ae9cee68f0 | |||
| eeece64417 | |||
| 243dc1f8a2 | |||
| 8c608440ae | |||
| 9aed1b082a | |||
| 6eb149f83a | |||
| 1243dbf0e1 | |||
| a15c2c27ff | |||
| aa441045aa | |||
| 569764d869 | |||
| 511b756694 | |||
| ab0c4d1315 | |||
| 6216abb0ab | |||
| 8c5b4fb298 | |||
| 03e9b8b208 | |||
| 60d48a0682 | |||
| dee6322751 | |||
| 5e3048a7bf | |||
| ac7aabf746 | |||
| ce30f0de57 | |||
| a58269fcdb | |||
| 3ecba8c8b4 | |||
| c98b673064 | |||
| 13fc64baa2 | |||
| 5e4b500785 | |||
| 693c5ed059 | |||
| a15405b6c5 | |||
| ce6f2b9512 | |||
| 63bb19747f | |||
| 0a6e1d4534 |
@@ -230,7 +230,7 @@
|
||||
A1115929188D686000641365 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 0500;
|
||||
LastUpgradeCheck = 0510;
|
||||
ORGANIZATIONNAME = "Thong Nguyen";
|
||||
TargetAttributes = {
|
||||
A111594B188D686000641365 = {
|
||||
@@ -332,7 +332,6 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
@@ -366,7 +365,7 @@
|
||||
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||
"$(SRCROOT)/../StreamingKit/StreamingKit",
|
||||
);
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 4.3;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
@@ -377,7 +376,6 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
@@ -405,7 +403,7 @@
|
||||
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||
"$(SRCROOT)/../StreamingKit/StreamingKit",
|
||||
);
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 4.3;
|
||||
SDKROOT = iphoneos;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
@@ -415,9 +413,9 @@
|
||||
A111595E188D686000641365 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREFIX_HEADER = "ExampleApp/ExampleApp-Prefix.pch";
|
||||
INFOPLIST_FILE = "ExampleApp/ExampleApp-Info.plist";
|
||||
@@ -432,7 +430,6 @@
|
||||
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;
|
||||
@@ -449,7 +446,6 @@
|
||||
A1115961188D686000641365 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
|
||||
BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/ExampleApp.app/ExampleApp";
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(SDKROOT)/Developer/Library/Frameworks",
|
||||
@@ -472,7 +468,6 @@
|
||||
A1115962188D686000641365 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
|
||||
BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/ExampleApp.app/ExampleApp";
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(SDKROOT)/Developer/Library/Frameworks",
|
||||
|
||||
@@ -21,24 +21,27 @@
|
||||
|
||||
@implementation AppDelegate
|
||||
|
||||
|
||||
-(BOOL) application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
|
||||
{
|
||||
NSError* error;
|
||||
|
||||
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&error];
|
||||
|
||||
[[AVAudioSession sharedInstance] setActive:YES error:&error];
|
||||
|
||||
Float32 bufferLength = 0.1;
|
||||
AudioSessionSetProperty(kAudioSessionProperty_PreferredHardwareIOBufferDuration, sizeof(bufferLength), &bufferLength);
|
||||
|
||||
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
|
||||
|
||||
self.window.backgroundColor = [UIColor whiteColor];
|
||||
|
||||
audioPlayer = [[STKAudioPlayer alloc] initWithOptions:STKAudioPlayerOptionFlushQueueOnSeek];
|
||||
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];
|
||||
AudioPlayerView* audioPlayerView = [[AudioPlayerView alloc] initWithFrame:self.window.bounds andAudioPlayer:audioPlayer];
|
||||
|
||||
audioPlayerView.delegate = self;
|
||||
audioPlayerView.audioPlayer = audioPlayer;
|
||||
|
||||
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
|
||||
[self becomeFirstResponder];
|
||||
|
||||
@@ -51,6 +51,7 @@
|
||||
UILabel* label;
|
||||
UILabel* statusLabel;
|
||||
UISlider* slider;
|
||||
UISwitch* enableEqSwitch;
|
||||
UISwitch* repeatSwitch;
|
||||
UIButton* muteButton;
|
||||
UIButton* playButton;
|
||||
@@ -65,4 +66,6 @@
|
||||
@property (readwrite, retain) STKAudioPlayer* audioPlayer;
|
||||
@property (readwrite, unsafe_unretained) id<AudioPlayerViewDelegate> delegate;
|
||||
|
||||
- (id)initWithFrame:(CGRect)frame andAudioPlayer:(STKAudioPlayer*)audioPlayer;
|
||||
|
||||
@end
|
||||
|
||||
@@ -47,12 +47,14 @@
|
||||
@implementation AudioPlayerView
|
||||
@synthesize audioPlayer, delegate;
|
||||
|
||||
- (id)initWithFrame:(CGRect)frame
|
||||
- (id)initWithFrame:(CGRect)frame andAudioPlayer:(STKAudioPlayer*)audioPlayerIn
|
||||
{
|
||||
self = [super initWithFrame:frame];
|
||||
|
||||
if (self)
|
||||
{
|
||||
self.audioPlayer = audioPlayerIn;
|
||||
|
||||
CGSize size = CGSizeMake(220, 50);
|
||||
|
||||
playFromHTTPButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
|
||||
@@ -97,7 +99,12 @@
|
||||
|
||||
size = CGSizeMake(80, 50);
|
||||
|
||||
repeatSwitch = [[UISwitch alloc] initWithFrame:CGRectMake((320 - size.width) / 2, frame.size.height * 0.15 + 180, size.width, size.height)];
|
||||
repeatSwitch = [[UISwitch alloc] initWithFrame:CGRectMake(30, frame.size.height * 0.15 + 180, size.width, size.height)];
|
||||
|
||||
enableEqSwitch = [[UISwitch alloc] initWithFrame:CGRectMake(320 - size.width - 30, frame.size.height * 0.15 + 180, size.width, size.height)];
|
||||
enableEqSwitch.on = audioPlayer.equalizerEnabled;
|
||||
|
||||
[enableEqSwitch addTarget:self action:@selector(onEnableEqSwitch) forControlEvents:UIControlEventAllTouchEvents];
|
||||
|
||||
label = [[UILabel alloc] initWithFrame:CGRectMake(0, slider.frame.origin.y + slider.frame.size.height + 10, frame.size.width, 25)];
|
||||
|
||||
@@ -107,11 +114,10 @@
|
||||
|
||||
statusLabel.textAlignment = NSTextAlignmentCenter;
|
||||
|
||||
|
||||
meter = [[UIView alloc] initWithFrame:CGRectMake(0, 450, 0, 20)];
|
||||
|
||||
meter.backgroundColor = [UIColor greenColor];
|
||||
|
||||
|
||||
[self addSubview:slider];
|
||||
[self addSubview:playButton];
|
||||
[self addSubview:playFromHTTPButton];
|
||||
@@ -124,6 +130,7 @@
|
||||
[self addSubview:stopButton];
|
||||
[self addSubview:meter];
|
||||
[self addSubview:muteButton];
|
||||
[self addSubview:enableEqSwitch];
|
||||
|
||||
[self setupTimer];
|
||||
[self updateControls];
|
||||
@@ -132,6 +139,11 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
-(void) onEnableEqSwitch
|
||||
{
|
||||
audioPlayer.equalizerEnabled = self->enableEqSwitch.on;
|
||||
}
|
||||
|
||||
-(void) sliderChanged
|
||||
{
|
||||
if (!audioPlayer)
|
||||
|
||||
@@ -432,7 +432,7 @@
|
||||
"$(SRCROOT)/../StreamingKit/StreamingKit",
|
||||
);
|
||||
INFOPLIST_FILE = "ExampleAppMac/ExampleAppMac-Info.plist";
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.8;
|
||||
MACOSX_DEPLOYMENT_TARGET = "";
|
||||
OTHER_LDFLAGS = "-ObjC";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
WRAPPER_EXTENSION = app;
|
||||
@@ -452,7 +452,7 @@
|
||||
"$(SRCROOT)/../StreamingKit/StreamingKit",
|
||||
);
|
||||
INFOPLIST_FILE = "ExampleAppMac/ExampleAppMac-Info.plist";
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.8;
|
||||
MACOSX_DEPLOYMENT_TARGET = "";
|
||||
OTHER_LDFLAGS = "-ObjC";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
WRAPPER_EXTENSION = app;
|
||||
|
||||
@@ -40,13 +40,22 @@
|
||||
[[self.window contentView] addSubview:playFromHTTPButton];
|
||||
[[self.window contentView] addSubview:meter];
|
||||
|
||||
audioPlayer = [[STKAudioPlayer alloc] init];
|
||||
audioPlayer = [[STKAudioPlayer alloc] initWithOptions:(STKAudioPlayerOptions){ .enableVolumeMixer = NO, .equalizerBandFrequencies = {50, 100, 200, 400, 800, 1600, 2600, 16000} } ];
|
||||
audioPlayer.delegate = self;
|
||||
audioPlayer.meteringEnabled = YES;
|
||||
|
||||
audioPlayer.volume = 0.1;
|
||||
|
||||
[self performSelector:@selector(test) withObject:nil afterDelay:4];
|
||||
[self performSelector:@selector(test) withObject:nil afterDelay:8];
|
||||
|
||||
[NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:@selector(tick:) userInfo:nil repeats:YES];
|
||||
}
|
||||
|
||||
-(void) test
|
||||
{
|
||||
audioPlayer.equalizerEnabled = !audioPlayer.equalizerEnabled;
|
||||
}
|
||||
|
||||
-(void) playFromHTTP
|
||||
{
|
||||
[audioPlayer play:@"http://fs.bloom.fm/oss/audiosamples/sample.mp3"];
|
||||
|
||||
@@ -16,7 +16,9 @@ The primary motivation of this project was to decouple the input data sources fr
|
||||
* Optimised for low CPU/battery usage (0% - 1% CPU usage when streaming).
|
||||
* Optimised for linear data sources. Random access sources are required only for seeking.
|
||||
* StreamingKit 0.2.0 uses the AudioUnit API rather than the slower AudioQueues API which allows real-time interception of the raw PCM data for features such as level metering, EQ, etc.
|
||||
* Example apps iOS and Mac OSX provided.
|
||||
* Power metering
|
||||
* Inbuilt equalizer/EQ (iOS 5.0 and above, OSX 10.9 Mavericks and above) with support for dynamically changing/enabling/disabling EQ while playing.
|
||||
* Example apps for iOS and Mac OSX provided.
|
||||
|
||||
## Installation
|
||||
|
||||
|
||||
@@ -401,7 +401,7 @@
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
CLASSPREFIX = STK;
|
||||
LastUpgradeCheck = 0500;
|
||||
LastUpgradeCheck = 0510;
|
||||
ORGANIZATIONNAME = "Thong Nguyen";
|
||||
};
|
||||
buildConfigurationList = A1E7C4C3188D57F50010896F /* Build configuration list for PBXProject "StreamingKit" */;
|
||||
@@ -565,7 +565,7 @@
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.8;
|
||||
MACOSX_DEPLOYMENT_TARGET = "";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = macosx;
|
||||
};
|
||||
@@ -582,7 +582,7 @@
|
||||
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREFIX_HEADER = "StreamingKitMac/StreamingKitMac-Prefix.pch";
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.8;
|
||||
MACOSX_DEPLOYMENT_TARGET = "";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = macosx;
|
||||
};
|
||||
@@ -635,7 +635,6 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
@@ -663,7 +662,7 @@
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 6.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 4.3;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
};
|
||||
@@ -673,7 +672,6 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
@@ -695,7 +693,7 @@
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 6.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 4.3;
|
||||
SDKROOT = iphoneos;
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
@@ -704,7 +702,6 @@
|
||||
A1E7C4EC188D57F60010896F /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
|
||||
DSTROOT = /tmp/StreamingKit.dst;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
@@ -721,7 +718,6 @@
|
||||
A1E7C4ED188D57F60010896F /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
|
||||
DSTROOT = /tmp/StreamingKit.dst;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
@@ -738,7 +734,6 @@
|
||||
A1E7C4EF188D57F60010896F /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(SDKROOT)/Developer/Library/Frameworks",
|
||||
"$(inherited)",
|
||||
@@ -759,7 +754,6 @@
|
||||
A1E7C4F0188D57F60010896F /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(SDKROOT)/Developer/Library/Frameworks",
|
||||
"$(inherited)",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -38,7 +38,7 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <pthread.h>
|
||||
#import "STKDataSource.h"
|
||||
#include <AudioToolbox/AudioToolbox.h>
|
||||
#import <AudioToolbox/AudioToolbox.h>
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
#include "UIKit/UIApplication.h"
|
||||
@@ -62,6 +62,8 @@ typedef enum
|
||||
STKAudioPlayerStopReasonNone = 0,
|
||||
STKAudioPlayerStopReasonEof,
|
||||
STKAudioPlayerStopReasonUserAction,
|
||||
STKAudioPlayerStopReasonPendingNext,
|
||||
STKAudioPlayerStopReasonDisposed,
|
||||
STKAudioPlayerStopReasonError = 0xffff
|
||||
}
|
||||
STKAudioPlayerStopReason;
|
||||
@@ -78,10 +80,24 @@ typedef enum
|
||||
}
|
||||
STKAudioPlayerErrorCode;
|
||||
|
||||
typedef enum
|
||||
typedef struct
|
||||
{
|
||||
STKAudioPlayerOptionNone = 0,
|
||||
STKAudioPlayerOptionFlushQueueOnSeek = 1
|
||||
/// If YES then seeking a track will cause all pending items to be flushed from the queue
|
||||
BOOL flushQueueOnSeek;
|
||||
/// If YES then volume control will be enabled on iOS
|
||||
BOOL enableVolumeMixer;
|
||||
/// A pointer to a 0 terminated array of band frequencies (iOS 5.0 and later, OSX 10.9 and later)
|
||||
Float32 equalizerBandFrequencies[24];
|
||||
/// The size of the internal I/O read buffer. This data in this buffer is transient and does not need to be larger.
|
||||
UInt32 readBufferSize;
|
||||
/// The size of the decompressed buffer (Default is 10 seconds which uses about 1.7MB of RAM)
|
||||
UInt32 bufferSizeInSeconds;
|
||||
/// Number of seconds of decompressed audio is required before playback first starts for each item (Default is 0.5 seconds. Must be larger than bufferSizeInSeconds)
|
||||
Float32 secondsRequiredToStartPlaying;
|
||||
/// Seconds after a seek is performed before data needs to come in (after which the state will change to playing/buffering)
|
||||
Float32 gracePeriodAfterSeekInSeconds;
|
||||
/// Number of seconds of decompressed audio required before playback resumes after a buffer underrun (Default is 5 seconds. Must be larger than bufferSizeinSeconds)
|
||||
Float32 secondsRequiredToStartPlayingAfterBufferUnderun;
|
||||
}
|
||||
STKAudioPlayerOptions;
|
||||
|
||||
@@ -117,6 +133,9 @@ typedef void(^STKFrameFilter)(UInt32 channelsPerFrame, UInt32 bytesPerFrame, UIn
|
||||
|
||||
@interface STKAudioPlayer : NSObject<STKDataSourceDelegate>
|
||||
|
||||
/// Gets or sets the volume (ranges 0 - 1.0).
|
||||
/// On iOS the STKAudioPlayerOptionEnableMultichannelMixer option must be enabled for volume to work.
|
||||
@property (readwrite) Float32 volume;
|
||||
/// Gets or sets the player muted state
|
||||
@property (readwrite) BOOL muted;
|
||||
/// Gets the current item duration in seconds
|
||||
@@ -125,6 +144,8 @@ typedef void(^STKFrameFilter)(UInt32 channelsPerFrame, UInt32 bytesPerFrame, UIn
|
||||
@property (readonly) double progress;
|
||||
/// Enables or disables peak and average decibel meteting
|
||||
@property (readwrite) BOOL meteringEnabled;
|
||||
/// Enables or disables the EQ
|
||||
@property (readwrite) BOOL equalizerEnabled;
|
||||
/// Returns an array of STKFrameFilterEntry objects representing the filters currently in use
|
||||
@property (readonly) NSArray* frameFilters;
|
||||
/// Returns the items pending to be played (includes buffering and upcoming items but does not include the current item)
|
||||
@@ -239,4 +260,7 @@ typedef void(^STKFrameFilter)(UInt32 channelsPerFrame, UInt32 bytesPerFrame, UIn
|
||||
/// Return values are between -60 (low) and 0 (high).
|
||||
-(float) averagePowerInDecibelsForChannel:(NSUInteger)channelNumber;
|
||||
|
||||
/// Sets the gain value (from -96 low to +24 high) for an equalizer band (0 based index)
|
||||
-(void) setGain:(float)gain forEqualizerBand:(int)bandIndex;
|
||||
|
||||
@end
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -36,6 +36,13 @@
|
||||
#import "STKHTTPDataSource.h"
|
||||
#import "STKDataSourceWrapper.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int watchdogPeriodSeconds;
|
||||
int inactivePeriodBeforeReconnectSeconds;
|
||||
}
|
||||
STKAutoRecoveringHTTPDataSourceOptions;
|
||||
|
||||
@interface STKAutoRecoveringHTTPDataSource : STKDataSourceWrapper
|
||||
|
||||
-(id) initWithHTTPDataSource:(STKHTTPDataSource*)innerDataSource;
|
||||
|
||||
@@ -38,18 +38,38 @@
|
||||
#import <arpa/inet.h>
|
||||
#import <ifaddrs.h>
|
||||
#import <netdb.h>
|
||||
#import "mach/mach_time.h"
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <SystemConfiguration/SystemConfiguration.h>
|
||||
#import "STKAutoRecoveringHTTPDataSource.h"
|
||||
|
||||
#define MAX_IMMEDIATE_RECONNECT_ATTEMPTS (2)
|
||||
#define DEFAULT_WATCHDOG_PERIOD_SECONDS (8)
|
||||
#define DEFAULT_INACTIVE_PERIOD_BEFORE_RECONNECT_SECONDS (15)
|
||||
|
||||
static uint64_t GetTickCount(void)
|
||||
{
|
||||
static mach_timebase_info_data_t sTimebaseInfo;
|
||||
uint64_t machTime = mach_absolute_time();
|
||||
|
||||
if (sTimebaseInfo.denom == 0 )
|
||||
{
|
||||
(void) mach_timebase_info(&sTimebaseInfo);
|
||||
}
|
||||
|
||||
uint64_t millis = ((machTime / 1000000) * sTimebaseInfo.numer) / sTimebaseInfo.denom;
|
||||
|
||||
return millis;
|
||||
}
|
||||
|
||||
@interface STKAutoRecoveringHTTPDataSource()
|
||||
{
|
||||
int serial;
|
||||
int waitSeconds;
|
||||
NSTimer* timeoutTimer;
|
||||
BOOL waitingForNetwork;
|
||||
uint64_t ticksWhenLastDataReceived;
|
||||
SCNetworkReachabilityRef reachabilityRef;
|
||||
STKAutoRecoveringHTTPDataSourceOptions options;
|
||||
}
|
||||
|
||||
-(void) reachabilityChanged;
|
||||
@@ -66,6 +86,19 @@ static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReach
|
||||
}
|
||||
}
|
||||
|
||||
static void PopulateOptionsWithDefault(STKAutoRecoveringHTTPDataSourceOptions* options)
|
||||
{
|
||||
if (options->watchdogPeriodSeconds == 0)
|
||||
{
|
||||
options->watchdogPeriodSeconds = DEFAULT_WATCHDOG_PERIOD_SECONDS;
|
||||
}
|
||||
|
||||
if (options->inactivePeriodBeforeReconnectSeconds == 0)
|
||||
{
|
||||
options->inactivePeriodBeforeReconnectSeconds = DEFAULT_INACTIVE_PERIOD_BEFORE_RECONNECT_SECONDS;
|
||||
}
|
||||
}
|
||||
|
||||
@implementation STKAutoRecoveringHTTPDataSource
|
||||
|
||||
-(STKHTTPDataSource*) innerHTTPDataSource
|
||||
@@ -79,6 +112,11 @@ static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReach
|
||||
}
|
||||
|
||||
-(id) initWithHTTPDataSource:(STKHTTPDataSource*)innerDataSourceIn
|
||||
{
|
||||
return [self initWithHTTPDataSource:innerDataSourceIn andOptions:(STKAutoRecoveringHTTPDataSourceOptions){}];
|
||||
}
|
||||
|
||||
-(id) initWithHTTPDataSource:(STKHTTPDataSource*)innerDataSourceIn andOptions:(STKAutoRecoveringHTTPDataSourceOptions)optionsIn
|
||||
{
|
||||
if (self = [super initWithDataSource:innerDataSourceIn])
|
||||
{
|
||||
@@ -90,6 +128,10 @@ static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReach
|
||||
zeroAddress.sin_len = sizeof(zeroAddress);
|
||||
zeroAddress.sin_family = AF_INET;
|
||||
|
||||
PopulateOptionsWithDefault(&optionsIn);
|
||||
|
||||
self->options = optionsIn;
|
||||
|
||||
reachabilityRef = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr*)&zeroAddress);
|
||||
}
|
||||
|
||||
@@ -117,14 +159,70 @@ static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReach
|
||||
[super registerForEvents:runLoop];
|
||||
[self startNotifierOnRunLoop:runLoop];
|
||||
|
||||
if (timeoutTimer)
|
||||
{
|
||||
[timeoutTimer invalidate];
|
||||
timeoutTimer = nil;
|
||||
}
|
||||
|
||||
ticksWhenLastDataReceived = GetTickCount();
|
||||
|
||||
[self createTimeoutTimer];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
-(void) unregisterForEvents
|
||||
{
|
||||
[super unregisterForEvents];
|
||||
|
||||
[self stopNotifier];
|
||||
|
||||
[self destroyTimeoutTimer];
|
||||
}
|
||||
|
||||
-(void) timeoutTimerTick:(NSTimer*)timer
|
||||
{
|
||||
if (![self hasBytesAvailable])
|
||||
{
|
||||
if ([self hasGotNetworkConnection])
|
||||
{
|
||||
uint64_t currentTicks = GetTickCount();
|
||||
|
||||
if (((currentTicks - ticksWhenLastDataReceived) / 1000) >= options.inactivePeriodBeforeReconnectSeconds)
|
||||
{
|
||||
serial++;
|
||||
|
||||
NSLog(@"timeoutTimerTick %lld/%lld", self.position, self.length);
|
||||
|
||||
[self attemptReconnectWithSerial:@(serial)];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-(void) createTimeoutTimer
|
||||
{
|
||||
[self destroyTimeoutTimer];
|
||||
|
||||
NSRunLoop* runLoop = self.innerDataSource.eventsRunLoop;
|
||||
|
||||
if (runLoop == nil)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
timeoutTimer = [NSTimer timerWithTimeInterval:options.watchdogPeriodSeconds target:self selector:@selector(timeoutTimerTick:) userInfo:@(serial) repeats:YES];
|
||||
|
||||
[runLoop addTimer:timeoutTimer forMode:NSRunLoopCommonModes];
|
||||
}
|
||||
|
||||
-(void) destroyTimeoutTimer
|
||||
{
|
||||
if (timeoutTimer)
|
||||
{
|
||||
[timeoutTimer invalidate];
|
||||
timeoutTimer = nil;
|
||||
}
|
||||
}
|
||||
|
||||
-(void) stopNotifier
|
||||
@@ -148,6 +246,19 @@ static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReach
|
||||
return NO;
|
||||
}
|
||||
|
||||
-(void) seekToOffset:(int64_t)offset
|
||||
{
|
||||
ticksWhenLastDataReceived = GetTickCount();
|
||||
|
||||
[super seekToOffset:offset];
|
||||
}
|
||||
|
||||
-(void) close
|
||||
{
|
||||
[self destroyTimeoutTimer];
|
||||
[super close];
|
||||
}
|
||||
|
||||
-(void) dealloc
|
||||
{
|
||||
NSLog(@"STKAutoRecoveringHTTPDataSource dealloc");
|
||||
@@ -155,6 +266,7 @@ static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReach
|
||||
self.innerDataSource.delegate = nil;
|
||||
|
||||
[self stopNotifier];
|
||||
[self destroyTimeoutTimer];
|
||||
|
||||
[NSObject cancelPreviousPerformRequestsWithTarget:self];
|
||||
|
||||
@@ -170,15 +282,25 @@ static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReach
|
||||
{
|
||||
waitingForNetwork = NO;
|
||||
|
||||
NSLog(@"reachabilityChanged %lld/%lld", self.position, self.length);
|
||||
|
||||
serial++;
|
||||
|
||||
[self attemptReconnectWithSerial:@(serial)];
|
||||
}
|
||||
}
|
||||
|
||||
-(void) dataSourceDataAvailable:(STKDataSource*)dataSource
|
||||
{
|
||||
if (![self.innerDataSource hasBytesAvailable])
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
serial++;
|
||||
waitSeconds = 1;
|
||||
|
||||
ticksWhenLastDataReceived = GetTickCount();
|
||||
|
||||
[super dataSourceDataAvailable:dataSource];
|
||||
}
|
||||
|
||||
@@ -191,7 +313,10 @@ static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReach
|
||||
|
||||
NSLog(@"attemptReconnect %lld/%lld", self.position, self.length);
|
||||
|
||||
[self seekToOffset:self.position];
|
||||
if (self.innerDataSource.eventsRunLoop)
|
||||
{
|
||||
[self.innerDataSource reconnect];
|
||||
}
|
||||
}
|
||||
|
||||
-(void) attemptReconnectWithTimer:(NSTimer*)timer
|
||||
@@ -214,10 +339,14 @@ static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReach
|
||||
|
||||
if (runLoop == nil)
|
||||
{
|
||||
[self performSelector:@selector(attemptReconnectWithSerial:) withObject:@(serial) afterDelay:waitSeconds];
|
||||
// DataSource no longer used
|
||||
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
serial++;
|
||||
|
||||
NSTimer* timer = [NSTimer timerWithTimeInterval:waitSeconds target:self selector:@selector(attemptReconnectWithTimer:) userInfo:@(serial) repeats:NO];
|
||||
|
||||
[runLoop addTimer:timer forMode:NSRunLoopCommonModes];
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
-(BOOL) reregisterForEvents;
|
||||
|
||||
-(void) open;
|
||||
-(void) openCompleted;
|
||||
-(void) dataAvailable;
|
||||
-(void) eof;
|
||||
-(void) errorOccured;
|
||||
|
||||
@@ -49,6 +49,9 @@ static void ReadStreamCallbackProc(CFReadStreamRef stream, CFStreamEventType eve
|
||||
case kCFStreamEventHasBytesAvailable:
|
||||
[datasource dataAvailable];
|
||||
break;
|
||||
case kCFStreamEventOpenCompleted:
|
||||
[datasource openCompleted];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -101,6 +104,11 @@ static void ReadStreamCallbackProc(CFReadStreamRef stream, CFStreamEventType eve
|
||||
{
|
||||
if (stream)
|
||||
{
|
||||
if (eventsRunLoop)
|
||||
{
|
||||
[self unregisterForEvents];
|
||||
}
|
||||
|
||||
CFReadStreamClose(stream);
|
||||
CFRelease(stream);
|
||||
|
||||
@@ -112,7 +120,7 @@ static void ReadStreamCallbackProc(CFReadStreamRef stream, CFStreamEventType eve
|
||||
{
|
||||
}
|
||||
|
||||
-(void) seekToOffset:(long long)offset
|
||||
-(void) seekToOffset:(SInt64)offset
|
||||
{
|
||||
}
|
||||
|
||||
@@ -127,6 +135,8 @@ static void ReadStreamCallbackProc(CFReadStreamRef stream, CFStreamEventType eve
|
||||
{
|
||||
CFReadStreamSetClient(stream, kCFStreamEventHasBytesAvailable | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered, NULL, NULL);
|
||||
CFReadStreamUnscheduleFromRunLoop(stream, [eventsRunLoop getCFRunLoop], kCFRunLoopCommonModes);
|
||||
|
||||
eventsRunLoop = nil;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,10 +158,10 @@ static void ReadStreamCallbackProc(CFReadStreamRef stream, CFStreamEventType eve
|
||||
{
|
||||
eventsRunLoop = runLoop;
|
||||
|
||||
if (!stream)
|
||||
if (!stream)
|
||||
{
|
||||
[self open];
|
||||
|
||||
// Will register when they open or seek
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
@@ -184,4 +194,8 @@ static void ReadStreamCallbackProc(CFReadStreamRef stream, CFStreamEventType eve
|
||||
return 0;
|
||||
}
|
||||
|
||||
-(void) openCompleted
|
||||
{
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -43,15 +43,10 @@
|
||||
-(void) dataSourceEof:(STKDataSource*)dataSource;
|
||||
@end
|
||||
|
||||
@protocol AudioDataSource<NSObject>
|
||||
@property (readwrite) double averageBitRate;
|
||||
@property (readwrite) long long audioDataOffset;
|
||||
@end
|
||||
|
||||
@interface STKDataSource : NSObject
|
||||
|
||||
@property (readonly) long long position;
|
||||
@property (readonly) long long length;
|
||||
@property (readonly) SInt64 position;
|
||||
@property (readonly) SInt64 length;
|
||||
@property (readonly) BOOL hasBytesAvailable;
|
||||
@property (readwrite, unsafe_unretained) id<STKDataSourceDelegate> delegate;
|
||||
|
||||
@@ -59,7 +54,7 @@
|
||||
-(void) unregisterForEvents;
|
||||
-(void) close;
|
||||
|
||||
-(void) seekToOffset:(long long)offset;
|
||||
-(void) seekToOffset:(SInt64)offset;
|
||||
-(int) readIntoBuffer:(UInt8*)buffer withSize:(int)size;
|
||||
-(AudioFileTypeID) audioFileTypeHint;
|
||||
|
||||
|
||||
@@ -37,12 +37,12 @@
|
||||
@implementation STKDataSource
|
||||
@synthesize delegate;
|
||||
|
||||
-(long long) length
|
||||
-(SInt64) length
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
-(void) seekToOffset:(long long)offset
|
||||
-(void) seekToOffset:(SInt64)offset
|
||||
{
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
return -1;
|
||||
}
|
||||
|
||||
-(long long) position
|
||||
-(SInt64) position
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -66,7 +66,7 @@
|
||||
}
|
||||
|
||||
-(void) close
|
||||
{
|
||||
{
|
||||
}
|
||||
|
||||
-(BOOL) hasBytesAvailable
|
||||
|
||||
@@ -62,12 +62,12 @@
|
||||
self.innerDataSource.delegate = nil;
|
||||
}
|
||||
|
||||
-(long long) length
|
||||
-(SInt64) length
|
||||
{
|
||||
return self.innerDataSource.length;
|
||||
}
|
||||
|
||||
-(void) seekToOffset:(long long)offset
|
||||
-(void) seekToOffset:(SInt64)offset
|
||||
{
|
||||
return [self.innerDataSource seekToOffset:offset];
|
||||
}
|
||||
@@ -77,7 +77,7 @@
|
||||
return [self.innerDataSource readIntoBuffer:buffer withSize:size];
|
||||
}
|
||||
|
||||
-(long long) position
|
||||
-(SInt64) position
|
||||
{
|
||||
return self.innerDataSource.position;
|
||||
}
|
||||
|
||||
@@ -43,12 +43,13 @@ typedef void(^STKAsyncURLProvider)(STKHTTPDataSource* dataSource, BOOL forSeek,
|
||||
@interface STKHTTPDataSource : STKCoreFoundationDataSource
|
||||
|
||||
@property (readonly, retain) NSURL* url;
|
||||
@property (readwrite) UInt32 httpStatusCode;
|
||||
@property (readonly) UInt32 httpStatusCode;
|
||||
|
||||
+(AudioFileTypeID) audioFileTypeHintFromMimeType:(NSString*)fileExtension;
|
||||
-(id) initWithURL:(NSURL*)url;
|
||||
-(id) initWithURLProvider:(STKURLProvider)urlProvider;
|
||||
-(id) initWithAsyncURLProvider:(STKAsyncURLProvider)asyncUrlProvider;
|
||||
-(NSRunLoop*) eventsRunLoop;
|
||||
-(void) reconnect;
|
||||
|
||||
@end
|
||||
|
||||
@@ -38,10 +38,13 @@
|
||||
@interface STKHTTPDataSource()
|
||||
{
|
||||
@private
|
||||
long long seekStart;
|
||||
long long relativePosition;
|
||||
long long fileLength;
|
||||
UInt32 httpStatusCode;
|
||||
SInt64 seekStart;
|
||||
SInt64 relativePosition;
|
||||
SInt64 fileLength;
|
||||
int discontinuous;
|
||||
int requestSerialNumber;
|
||||
|
||||
NSURL* currentUrl;
|
||||
STKAsyncURLProvider asyncUrlProvider;
|
||||
NSDictionary* httpHeaders;
|
||||
@@ -138,21 +141,28 @@
|
||||
|
||||
-(void) dataAvailable
|
||||
{
|
||||
if (stream == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.httpStatusCode == 0)
|
||||
{
|
||||
CFTypeRef response = CFReadStreamCopyProperty(stream, kCFStreamPropertyHTTPResponseHeader);
|
||||
|
||||
httpHeaders = (__bridge_transfer NSDictionary*)CFHTTPMessageCopyAllHeaderFields((CFHTTPMessageRef)response);
|
||||
|
||||
self.httpStatusCode = CFHTTPMessageGetResponseStatusCode((CFHTTPMessageRef)response);
|
||||
|
||||
CFRelease(response);
|
||||
if (response)
|
||||
{
|
||||
httpHeaders = (__bridge_transfer NSDictionary*)CFHTTPMessageCopyAllHeaderFields((CFHTTPMessageRef)response);
|
||||
|
||||
self->httpStatusCode = (UInt32)CFHTTPMessageGetResponseStatusCode((CFHTTPMessageRef)response);
|
||||
|
||||
CFRelease(response);
|
||||
}
|
||||
|
||||
if (self.httpStatusCode == 200)
|
||||
{
|
||||
if (seekStart == 0)
|
||||
{
|
||||
fileLength = (long long)[[httpHeaders objectForKey:@"Content-Length"] integerValue];
|
||||
fileLength = (SInt64)[[httpHeaders objectForKey:@"Content-Length"] longLongValue];
|
||||
}
|
||||
|
||||
NSString* contentType = [httpHeaders objectForKey:@"Content-Type"];
|
||||
@@ -195,28 +205,34 @@
|
||||
[super dataAvailable];
|
||||
}
|
||||
|
||||
-(long long) position
|
||||
-(SInt64) position
|
||||
{
|
||||
return seekStart + relativePosition;
|
||||
}
|
||||
|
||||
-(long long) length
|
||||
-(SInt64) length
|
||||
{
|
||||
return fileLength >= 0 ? fileLength : 0;
|
||||
}
|
||||
|
||||
-(void) seekToOffset:(long long)offset
|
||||
-(void) reconnect
|
||||
{
|
||||
if (eventsRunLoop)
|
||||
{
|
||||
[self unregisterForEvents];
|
||||
}
|
||||
NSRunLoop* savedEventsRunLoop = eventsRunLoop;
|
||||
|
||||
if (stream)
|
||||
{
|
||||
CFReadStreamClose(stream);
|
||||
CFRelease(stream);
|
||||
}
|
||||
[self close];
|
||||
|
||||
eventsRunLoop = savedEventsRunLoop;
|
||||
|
||||
[self seekToOffset:self.position];
|
||||
}
|
||||
|
||||
-(void) seekToOffset:(SInt64)offset
|
||||
{
|
||||
NSRunLoop* savedEventsRunLoop = eventsRunLoop;
|
||||
|
||||
[self close];
|
||||
|
||||
eventsRunLoop = savedEventsRunLoop;
|
||||
|
||||
NSAssert([NSRunLoop currentRunLoop] == eventsRunLoop, @"Seek called on wrong thread");
|
||||
|
||||
@@ -255,8 +271,18 @@
|
||||
|
||||
-(void) openForSeek:(BOOL)forSeek
|
||||
{
|
||||
int localRequestSerialNumber;
|
||||
|
||||
requestSerialNumber++;
|
||||
localRequestSerialNumber = requestSerialNumber;
|
||||
|
||||
asyncUrlProvider(self, forSeek, ^(NSURL* url)
|
||||
{
|
||||
if (localRequestSerialNumber != self->requestSerialNumber)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self->currentUrl = url;
|
||||
|
||||
if (url == nil)
|
||||
@@ -317,7 +343,7 @@
|
||||
|
||||
[self reregisterForEvents];
|
||||
|
||||
self.httpStatusCode = 0;
|
||||
self->httpStatusCode = 0;
|
||||
|
||||
// Open
|
||||
|
||||
@@ -325,6 +351,8 @@
|
||||
{
|
||||
CFRelease(stream);
|
||||
CFRelease(message);
|
||||
|
||||
stream = 0;
|
||||
|
||||
[self errorOccured];
|
||||
|
||||
@@ -337,6 +365,11 @@
|
||||
});
|
||||
}
|
||||
|
||||
-(UInt32) httpStatusCode
|
||||
{
|
||||
return self->httpStatusCode;
|
||||
}
|
||||
|
||||
-(NSRunLoop*) eventsRunLoop
|
||||
{
|
||||
return self->eventsRunLoop;
|
||||
|
||||
@@ -36,8 +36,8 @@
|
||||
|
||||
@interface STKLocalFileDataSource()
|
||||
{
|
||||
long long position;
|
||||
long long length;
|
||||
SInt64 position;
|
||||
SInt64 length;
|
||||
AudioFileTypeID audioFileTypeHint;
|
||||
}
|
||||
@property (readwrite, copy) NSString* filePath;
|
||||
@@ -153,12 +153,12 @@
|
||||
CFReadStreamOpen(stream);
|
||||
}
|
||||
|
||||
-(long long) position
|
||||
-(SInt64) position
|
||||
{
|
||||
return position;
|
||||
}
|
||||
|
||||
-(long long) length
|
||||
-(SInt64) length
|
||||
{
|
||||
return length;
|
||||
}
|
||||
@@ -181,7 +181,7 @@
|
||||
return retval;
|
||||
}
|
||||
|
||||
-(void) seekToOffset:(long long)offset
|
||||
-(void) seekToOffset:(SInt64)offset
|
||||
{
|
||||
CFStreamStatus status = kCFStreamStatusClosed;
|
||||
|
||||
|
||||
@@ -22,9 +22,9 @@
|
||||
UInt64 audioDataByteCount;
|
||||
UInt32 packetBufferSize;
|
||||
volatile Float64 seekTime;
|
||||
volatile int64_t framesQueued;
|
||||
volatile int64_t framesPlayed;
|
||||
volatile int64_t lastFrameQueued;
|
||||
volatile SInt64 framesQueued;
|
||||
volatile SInt64 framesPlayed;
|
||||
volatile SInt64 lastFrameQueued;
|
||||
volatile int processedPacketsCount;
|
||||
volatile int processedPacketsSizeTotal;
|
||||
AudioStreamBasicDescription audioStreamBasicDescription;
|
||||
@@ -40,7 +40,6 @@
|
||||
-(double) duration;
|
||||
-(Float64) progressInFrames;
|
||||
-(double) calculatedBitRate;
|
||||
-(void) updateAudioDataSource;
|
||||
-(BOOL) isDefinitelyCompatible:(AudioStreamBasicDescription*)basicDescription;
|
||||
|
||||
@end
|
||||
@@ -9,7 +9,8 @@
|
||||
#import "STKQueueEntry.h"
|
||||
#import "STKDataSource.h"
|
||||
|
||||
#define STK_BIT_RATE_ESTIMATION_MIN_PACKETS (64)
|
||||
#define STK_BIT_RATE_ESTIMATION_MIN_PACKETS_MIN (2)
|
||||
#define STK_BIT_RATE_ESTIMATION_MIN_PACKETS_PREFERRED (64)
|
||||
|
||||
@implementation STKQueueEntry
|
||||
|
||||
@@ -38,13 +39,16 @@
|
||||
{
|
||||
double retval;
|
||||
|
||||
if (packetDuration && processedPacketsCount > STK_BIT_RATE_ESTIMATION_MIN_PACKETS)
|
||||
if (packetDuration > 0)
|
||||
{
|
||||
double averagePacketByteSize = processedPacketsSizeTotal / processedPacketsCount;
|
||||
|
||||
retval = averagePacketByteSize / packetDuration * 8;
|
||||
|
||||
return retval;
|
||||
if (processedPacketsCount > STK_BIT_RATE_ESTIMATION_MIN_PACKETS_PREFERRED || (audioStreamBasicDescription.mBytesPerFrame == 0 && processedPacketsCount > STK_BIT_RATE_ESTIMATION_MIN_PACKETS_MIN))
|
||||
{
|
||||
double averagePacketByteSize = processedPacketsSizeTotal / processedPacketsCount;
|
||||
|
||||
retval = averagePacketByteSize / packetDuration * 8;
|
||||
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
retval = (audioStreamBasicDescription.mBytesPerFrame * audioStreamBasicDescription.mSampleRate) * 8;
|
||||
@@ -52,19 +56,6 @@
|
||||
return retval;
|
||||
}
|
||||
|
||||
-(void) updateAudioDataSource
|
||||
{
|
||||
if ([self.dataSource conformsToProtocol:@protocol(AudioDataSource)])
|
||||
{
|
||||
double calculatedBitrate = [self calculatedBitRate];
|
||||
|
||||
id<AudioDataSource> audioDataSource = (id<AudioDataSource>)self.dataSource;
|
||||
|
||||
audioDataSource.averageBitRate = calculatedBitrate;
|
||||
audioDataSource.audioDataOffset = audioDataOffset;
|
||||
}
|
||||
}
|
||||
|
||||
-(double) duration
|
||||
{
|
||||
if (self->sampleRate <= 0)
|
||||
|
||||
Reference in New Issue
Block a user