Compare commits

..

32 Commits

Author SHA1 Message Date
Thong Nguyen da71b04aaf Updated version number in podspec 2015-12-07 13:02:44 +00:00
Thong Nguyen 55a314b966 Fix int->double truncation when doing duration calculation 2015-12-07 13:00:53 +00:00
Thong Nguyen 8fa821a944 Fixed int->double truncation in duration calculation 2015-12-07 12:59:32 +00:00
Thong Nguyen 0f69b7ea76 Merge pull request #239 from reindernijhoff/master
Fixed memory leak
2015-12-06 14:00:49 +00:00
Reinder Nijhoff 4d0fccdd70 Fixed memory leak 2015-12-06 14:06:15 +01:00
Thong Nguyen 5909657368 Changed enums to NS_ENUM to better support Swift. Added launch images to remove warnings 2015-12-04 22:47:14 +00:00
Thong Nguyen 50bec46acc Fixed build issue with OSSTATUS_PRINTF_VALUE on 64bit 2015-12-03 17:25:30 +00:00
Thong Nguyen dec8b87498 Updated podspec version 2015-12-03 16:18:50 +00:00
Thong Nguyen f872de223d Added support for disabling buffers in options using STK_DISABLE_BUFFER. Changed bufferSizeInSeconds from a UInt32 to Float32 2015-12-03 16:18:06 +00:00
Thong Nguyen 4f72249c94 Fixed License 2015-10-29 18:16:01 +00:00
Thong Nguyen 39f0d8bdfe Added CA_CANONICAL_DEPRECATED check 2014-12-29 18:14:39 +00:00
Thong Nguyen 8708e48395 Merge pull request #166 from danielgindi/master
NULLing eventsRunLoop may crash later
2014-12-29 18:10:15 +00:00
Daniel Cohen Gindi 94e6cbf41b NULLing eventsRunLoop may crash later 2014-12-29 20:05:21 +02:00
Thong Nguyen a5b6360b1c Merge pull request #152 from danielgindi/recording
Recording
2014-12-29 17:51:26 +00:00
Daniel Cohen Gindi 09e5602464 Allow recording a specific DataSource to AAC-LC 2014-12-29 19:43:29 +02:00
Thong Nguyen 4c7ce6c4ec Merge branch 'danielgindi-duration_and_progress' 2014-12-29 16:51:09 +00:00
Thong Nguyen 5784504e38 Merge branch 'duration_and_progress' of git://github.com/danielgindi/StreamingKit into danielgindi-duration_and_progress 2014-12-29 16:50:36 +00:00
Thong Nguyen e73df7fd86 Merge branch 'danielgindi-deprecations' 2014-12-29 16:49:00 +00:00
Thong Nguyen 9a4b4a617d Merged/fixed deprecations changes from danielgindi 2014-12-29 16:48:37 +00:00
Thong Nguyen 7d4d7b847e Merge pull request #153 from danielgindi/quality
Transcode using the same number of channels as the datasource
2014-12-29 16:41:23 +00:00
Daniel Cohen Gindi 61cb8a8a7b Transcode using the same number of channels as the datasource
We also need to do the same for sample rate, but then we need to restart the audio unit graph
2014-12-18 00:51:01 +02:00
Daniel Cohen Gindi 8e2e451e9c These SSL constants are deprecated since iOS 4.0 2014-12-17 17:31:54 +02:00
Daniel Cohen Gindi 88be5b33c6 Using AudioSampleType constant is deprecated in iOS 8
The equivalent is SInt16
2014-12-17 17:31:50 +02:00
Daniel Cohen Gindi 50a8b610c4 Provide a mechanism to hint for correct duration.
For cases where duration can't be calculated accurately
2014-12-17 17:31:20 +02:00
Thong Nguyen c1b3b5d8cc Merge pull request #141 from danielgindi/master
Allow using headers from submodule, framework-like
2014-11-10 20:28:53 +00:00
Daniel Cohen Gindi 477b1f175f Allow using headers from submodule, framework-like
Now when we use the xcodeproj as a submodule we can do stuff like:
#import <StreamingKit/STKAudioPlayer.h>
without setting up weird include paths.
2014-11-10 21:05:23 +02:00
Thong Nguyen 000930a295 Fixed seek not working with HTTP 2014-11-10 17:25:02 +00:00
Thong Nguyen 269f335ee4 Smallf ix to ExampleAppMac app 2014-11-08 20:56:55 +00:00
Thong Nguyen 162d964372 STKHTTPDataSource tidyup 2014-11-08 20:04:29 +00:00
Thong Nguyen ef7f42c97e AudioPlayerView layout jig 2014-11-08 19:52:54 +00:00
Thong Nguyen ea9e40b17a Added progress for live streams in AudioPlayerView 2014-11-08 19:45:26 +00:00
Thong Nguyen 9510d74e58 Added progress for live streams in AudioPlayerView 2014-11-08 19:43:51 +00:00
27 changed files with 518 additions and 101 deletions
@@ -230,7 +230,7 @@
A1115929188D686000641365 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0510;
LastUpgradeCheck = 0710;
ORGANIZATIONNAME = "Thong Nguyen";
TargetAttributes = {
A111594B188D686000641365 = {
@@ -346,6 +346,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_OPTIMIZATION_LEVEL = 0;
@@ -422,6 +423,7 @@
IPHONEOS_DEPLOYMENT_TARGET = 6.0;
LLVM_LTO = YES;
OTHER_LDFLAGS = "-ObjC";
PRODUCT_BUNDLE_IDENTIFIER = "abstractpath.com.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = app;
};
@@ -438,6 +440,7 @@
IPHONEOS_DEPLOYMENT_TARGET = 6.0;
LLVM_LTO = YES;
OTHER_LDFLAGS = "-ObjC";
PRODUCT_BUNDLE_IDENTIFIER = "abstractpath.com.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = app;
};
@@ -459,6 +462,7 @@
"$(inherited)",
);
INFOPLIST_FILE = "ExampleAppTests/ExampleAppTests-Info.plist";
PRODUCT_BUNDLE_IDENTIFIER = "abstractpath.com.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUNDLE_LOADER)";
WRAPPER_EXTENSION = xctest;
@@ -477,6 +481,7 @@
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "ExampleApp/ExampleApp-Prefix.pch";
INFOPLIST_FILE = "ExampleAppTests/ExampleAppTests-Info.plist";
PRODUCT_BUNDLE_IDENTIFIER = "abstractpath.com.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUNDLE_LOADER)";
WRAPPER_EXTENSION = xctest;
+3 -3
View File
@@ -32,6 +32,7 @@
AudioSessionSetProperty(kAudioSessionProperty_PreferredHardwareIOBufferDuration, sizeof(bufferLength), &bufferLength);
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.rootViewController = [[UIViewController alloc] init];
self.window.backgroundColor = [UIColor whiteColor];
@@ -39,7 +40,6 @@
audioPlayer.meteringEnabled = YES;
audioPlayer.volume = 1;
AudioPlayerView* audioPlayerView = [[AudioPlayerView alloc] initWithFrame:self.window.bounds andAudioPlayer:audioPlayer];
audioPlayerView.delegate = self;
@@ -47,9 +47,9 @@
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[self becomeFirstResponder];
[self.window addSubview:audioPlayerView];
[self.window makeKeyAndVisible];
[self.window.rootViewController.view addSubview:audioPlayerView];
return YES;
}
+20 -9
View File
@@ -58,27 +58,27 @@
CGSize size = CGSizeMake(220, 50);
playFromHTTPButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
playFromHTTPButton.frame = CGRectMake((320 - size.width) / 2, frame.size.height * 0.10, size.width, size.height);
playFromHTTPButton.frame = CGRectMake((frame.size.width - size.width) / 2, frame.size.height * 0.10, size.width, size.height);
[playFromHTTPButton addTarget:self action:@selector(playFromHTTPButtonTouched) forControlEvents:UIControlEventTouchUpInside];
[playFromHTTPButton setTitle:@"Play from HTTP" forState:UIControlStateNormal];
playFromIcecastButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
playFromIcecastButton.frame = CGRectMake((320 - size.width) / 2, frame.size.height * 0.10 + 50, size.width, size.height);
playFromIcecastButton.frame = CGRectMake((frame.size.width - size.width) / 2, frame.size.height * 0.10 + 35, size.width, size.height);
[playFromIcecastButton addTarget:self action:@selector(playFromIcecasButtonTouched) forControlEvents:UIControlEventTouchUpInside];
[playFromIcecastButton setTitle:@"Play from Icecast" forState:UIControlStateNormal];
playFromLocalFileButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
playFromLocalFileButton.frame = CGRectMake((320 - size.width) / 2, frame.size.height * 0.10 + 100, size.width, size.height);
playFromLocalFileButton.frame = CGRectMake((frame.size.width - size.width) / 2, frame.size.height * 0.10 + 70, size.width, size.height);
[playFromLocalFileButton addTarget:self action:@selector(playFromLocalFileButtonTouched) forControlEvents:UIControlEventTouchUpInside];
[playFromLocalFileButton setTitle:@"Play from Local File" forState:UIControlStateNormal];
queueShortFileButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
queueShortFileButton.frame = CGRectMake((320 - size.width) / 2, frame.size.height * 0.10 + 150, size.width, size.height);
queueShortFileButton.frame = CGRectMake((frame.size.width - size.width) / 2, frame.size.height * 0.10 + 105, size.width, size.height);
[queueShortFileButton addTarget:self action:@selector(queueShortFileButtonTouched) forControlEvents:UIControlEventTouchUpInside];
[queueShortFileButton setTitle:@"Queue short file" forState:UIControlStateNormal];
queuePcmWaveFileFromHTTPButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
queuePcmWaveFileFromHTTPButton.frame = CGRectMake((320 - size.width) / 2, frame.size.height * 0.10 + 280, size.width, size.height);
queuePcmWaveFileFromHTTPButton.frame = CGRectMake((frame.size.width - size.width) / 2, frame.size.height * 0.10 + 140, size.width, size.height);
[queuePcmWaveFileFromHTTPButton addTarget:self action:@selector(queuePcmWaveFileButtonTouched) forControlEvents:UIControlEventTouchUpInside];
[queuePcmWaveFileFromHTTPButton setTitle:@"Queue PCM/WAVE from HTTP" forState:UIControlStateNormal];
@@ -89,12 +89,12 @@
[playButton addTarget:self action:@selector(playButtonPressed) forControlEvents:UIControlEventTouchUpInside];
stopButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
stopButton.frame = CGRectMake((320 - size.width) - 30, 400, size.width, size.height);
stopButton.frame = CGRectMake((frame.size.width - size.width) - 30, 400, size.width, size.height);
[stopButton addTarget:self action:@selector(stopButtonPressed) forControlEvents:UIControlEventTouchUpInside];
[stopButton setTitle:@"Stop" forState:UIControlStateNormal];
muteButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
muteButton.frame = CGRectMake((320 - size.width) - 30, 420, size.width, size.height);
muteButton.frame = CGRectMake((frame.size.width - size.width) - 30, 430, size.width, size.height);
[muteButton addTarget:self action:@selector(muteButtonPressed) forControlEvents:UIControlEventTouchUpInside];
[muteButton setTitle:@"Mute" forState:UIControlStateNormal];
@@ -106,7 +106,7 @@
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 = [[UISwitch alloc] initWithFrame:CGRectMake(frame.size.width - 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];
@@ -180,6 +180,17 @@
return;
}
if (audioPlayer.currentlyPlayingQueueItemId == nil)
{
slider.value = 0;
slider.minimumValue = 0;
slider.maximumValue = 0;
label.text = @"";
return;
}
if (audioPlayer.duration != 0)
{
slider.minimumValue = 0;
@@ -194,7 +205,7 @@
slider.minimumValue = 0;
slider.maximumValue = 0;
label.text = @"";
label.text = [NSString stringWithFormat:@"Live stream %@", [self formatTimeFromSeconds:audioPlayer.progress]];
}
statusLabel.text = audioPlayer.state == STKAudioPlayerStateBuffering ? @"buffering" : @"";
+1 -1
View File
@@ -9,7 +9,7 @@
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>abstractpath.com.${PRODUCT_NAME:rfc1034identifier}</string>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
@@ -5,16 +5,31 @@
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "3x"
},
{
"idiom" : "ipad",
"size" : "29x29",
@@ -0,0 +1,6 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}
@@ -1,5 +1,31 @@
{
"images" : [
{
"extent" : "full-screen",
"idiom" : "iphone",
"subtype" : "736h",
"filename" : "TX6sV.png",
"minimum-system-version" : "8.0",
"orientation" : "portrait",
"scale" : "3x"
},
{
"orientation" : "landscape",
"idiom" : "iphone",
"extent" : "full-screen",
"minimum-system-version" : "8.0",
"subtype" : "736h",
"scale" : "3x"
},
{
"extent" : "full-screen",
"idiom" : "iphone",
"subtype" : "667h",
"filename" : "dBEHd.png",
"minimum-system-version" : "8.0",
"orientation" : "portrait",
"scale" : "2x"
},
{
"orientation" : "portrait",
"idiom" : "iphone",
@@ -8,11 +34,12 @@
"scale" : "2x"
},
{
"orientation" : "portrait",
"extent" : "full-screen",
"idiom" : "iphone",
"subtype" : "retina4",
"extent" : "full-screen",
"filename" : "TX6sV-2.png",
"minimum-system-version" : "7.0",
"orientation" : "portrait",
"scale" : "2x"
},
{
@@ -42,6 +69,26 @@
"extent" : "full-screen",
"minimum-system-version" : "7.0",
"scale" : "2x"
},
{
"orientation" : "portrait",
"idiom" : "iphone",
"extent" : "full-screen",
"scale" : "1x"
},
{
"orientation" : "portrait",
"idiom" : "iphone",
"extent" : "full-screen",
"scale" : "2x"
},
{
"orientation" : "portrait",
"idiom" : "iphone",
"filename" : "TX6sV-1.png",
"extent" : "full-screen",
"subtype" : "retina4",
"scale" : "2x"
}
],
"info" : {
Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

@@ -7,7 +7,7 @@
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>abstractpath.com.${PRODUCT_NAME:rfc1034identifier}</string>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
+1 -1
View File
@@ -72,7 +72,7 @@
CGFloat meterWidth = 0;
if (audioPlayer.duration != 0)
if (audioPlayer.currentlyPlayingQueueItemId != nil)
{
slider.minValue = 0;
slider.maxValue = audioPlayer.duration;
+3 -3
View File
@@ -4,7 +4,7 @@
Inspired by Matt Gallagher's AudioStreamer:
https://github.com/mattgallagher/AudioStreamer
Copyright (c) 2012 Thong Nguyen (tumtumtum@gmail.com). All rights reserved.
Copyright (c) 2015 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:
@@ -15,12 +15,12 @@
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 the <organization>.
This product includes software developed by Thong Nguyen.
4. Neither the name of the <organization> 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 <COPYRIGHT HOLDER> ''AS IS'' AND ANY
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 <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+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.27"
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,14 @@
objects = {
/* Begin PBXBuildFile section */
5B949CD21A1140E4005675A0 /* STKAudioPlayer.h in Headers */ = {isa = PBXBuildFile; fileRef = A1E7C4F1188D5E550010896F /* STKAudioPlayer.h */; settings = {ATTRIBUTES = (Public, ); }; };
5B949CD31A1140E4005675A0 /* STKAutoRecoveringHTTPDataSource.h in Headers */ = {isa = PBXBuildFile; fileRef = A1E7C4F3188D5E550010896F /* STKAutoRecoveringHTTPDataSource.h */; settings = {ATTRIBUTES = (Public, ); }; };
5B949CD41A1140E4005675A0 /* STKCoreFoundationDataSource.h in Headers */ = {isa = PBXBuildFile; fileRef = A1E7C4F5188D5E550010896F /* STKCoreFoundationDataSource.h */; settings = {ATTRIBUTES = (Public, ); }; };
5B949CD51A1140E4005675A0 /* STKDataSource.h in Headers */ = {isa = PBXBuildFile; fileRef = A1E7C4F7188D5E550010896F /* STKDataSource.h */; settings = {ATTRIBUTES = (Public, ); }; };
5B949CD61A1140E4005675A0 /* STKDataSourceWrapper.h in Headers */ = {isa = PBXBuildFile; fileRef = A1E7C4F9188D5E550010896F /* STKDataSourceWrapper.h */; settings = {ATTRIBUTES = (Public, ); }; };
5B949CD71A1140E4005675A0 /* STKHTTPDataSource.h in Headers */ = {isa = PBXBuildFile; fileRef = A1E7C4FB188D5E550010896F /* STKHTTPDataSource.h */; settings = {ATTRIBUTES = (Public, ); }; };
5B949CD81A1140E4005675A0 /* STKLocalFileDataSource.h in Headers */ = {isa = PBXBuildFile; fileRef = A1E7C4FD188D5E550010896F /* STKLocalFileDataSource.h */; settings = {ATTRIBUTES = (Public, ); }; };
5B949CD91A1140E4005675A0 /* STKQueueEntry.h in Headers */ = {isa = PBXBuildFile; fileRef = A1BF65D0189A6582004DD08C /* STKQueueEntry.h */; settings = {ATTRIBUTES = (Public, ); }; };
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 */; };
@@ -314,6 +322,21 @@
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
5B949CD11A1140CF005675A0 /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
5B949CD21A1140E4005675A0 /* STKAudioPlayer.h in Headers */,
5B949CD31A1140E4005675A0 /* STKAutoRecoveringHTTPDataSource.h in Headers */,
5B949CD41A1140E4005675A0 /* STKCoreFoundationDataSource.h in Headers */,
5B949CD51A1140E4005675A0 /* STKDataSource.h in Headers */,
5B949CD61A1140E4005675A0 /* STKDataSourceWrapper.h in Headers */,
5B949CD71A1140E4005675A0 /* STKHTTPDataSource.h in Headers */,
5B949CD81A1140E4005675A0 /* STKLocalFileDataSource.h in Headers */,
5B949CD91A1140E4005675A0 /* STKQueueEntry.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
A1A49967189E744400E2A2E2 /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
@@ -366,6 +389,7 @@
A1E7C4C4188D57F50010896F /* Sources */,
A1E7C4C5188D57F50010896F /* Frameworks */,
A1E7C4C6188D57F50010896F /* CopyFiles */,
5B949CD11A1140CF005675A0 /* Headers */,
);
buildRules = (
);
@@ -401,7 +425,7 @@
isa = PBXProject;
attributes = {
CLASSPREFIX = STK;
LastUpgradeCheck = 0610;
LastUpgradeCheck = 0710;
ORGANIZATIONNAME = "Thong Nguyen";
};
buildConfigurationList = A1E7C4C3188D57F50010896F /* Build configuration list for PBXProject "StreamingKit" */;
@@ -568,6 +592,7 @@
);
MACOSX_DEPLOYMENT_TARGET = "";
PRODUCT_NAME = "$(TARGET_NAME)";
PUBLIC_HEADERS_FOLDER_PATH = include/StreamingKit;
SDKROOT = macosx;
};
name = Debug;
@@ -586,6 +611,7 @@
GCC_PREFIX_HEADER = "StreamingKitMac/StreamingKitMac-Prefix.pch";
MACOSX_DEPLOYMENT_TARGET = "";
PRODUCT_NAME = "$(TARGET_NAME)";
PUBLIC_HEADERS_FOLDER_PATH = include/StreamingKit;
SDKROOT = macosx;
};
name = Release;
@@ -607,6 +633,7 @@
);
INFOPLIST_FILE = "StreamingKitMacTests/StreamingKitMacTests-Info.plist";
MACOSX_DEPLOYMENT_TARGET = 10.8;
PRODUCT_BUNDLE_IDENTIFIER = "com.abstractpath.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = macosx;
WRAPPER_EXTENSION = xctest;
@@ -627,6 +654,7 @@
GCC_PREFIX_HEADER = "StreamingKitMac/StreamingKitMac-Prefix.pch";
INFOPLIST_FILE = "StreamingKitMacTests/StreamingKitMacTests-Info.plist";
MACOSX_DEPLOYMENT_TARGET = 10.8;
PRODUCT_BUNDLE_IDENTIFIER = "com.abstractpath.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = macosx;
WRAPPER_EXTENSION = xctest;
@@ -650,6 +678,7 @@
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_OPTIMIZATION_LEVEL = 0;
@@ -713,6 +742,7 @@
GCC_PREFIX_HEADER = "StreamingKit/StreamingKit-Prefix.pch";
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
PUBLIC_HEADERS_FOLDER_PATH = include/StreamingKit;
SKIP_INSTALL = YES;
};
name = Debug;
@@ -729,6 +759,7 @@
GCC_PREFIX_HEADER = "StreamingKit/StreamingKit-Prefix.pch";
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
PUBLIC_HEADERS_FOLDER_PATH = include/StreamingKit;
SKIP_INSTALL = YES;
};
name = Release;
@@ -748,6 +779,7 @@
"$(inherited)",
);
INFOPLIST_FILE = "StreamingKitTests/StreamingKitTests-Info.plist";
PRODUCT_BUNDLE_IDENTIFIER = "abstractpath.com.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = xctest;
};
@@ -764,6 +796,7 @@
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "StreamingKit/StreamingKit-Prefix.pch";
INFOPLIST_FILE = "StreamingKitTests/StreamingKitTests-Info.plist";
PRODUCT_BUNDLE_IDENTIFIER = "abstractpath.com.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = xctest;
};
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>
+14 -10
View File
@@ -44,7 +44,7 @@
#include "UIKit/UIApplication.h"
#endif
typedef enum
typedef NS_OPTIONS(NSInteger, STKAudioPlayerState)
{
STKAudioPlayerStateReady,
STKAudioPlayerStateRunning = 1,
@@ -54,10 +54,9 @@ typedef enum
STKAudioPlayerStateStopped = (1 << 4),
STKAudioPlayerStateError = (1 << 5),
STKAudioPlayerStateDisposed = (1 << 6)
}
STKAudioPlayerState;
};
typedef enum
typedef NS_ENUM(NSInteger, STKAudioPlayerStopReason)
{
STKAudioPlayerStopReasonNone = 0,
STKAudioPlayerStopReasonEof,
@@ -65,10 +64,9 @@ typedef enum
STKAudioPlayerStopReasonPendingNext,
STKAudioPlayerStopReasonDisposed,
STKAudioPlayerStopReasonError = 0xffff
}
STKAudioPlayerStopReason;
};
typedef enum
typedef NS_ENUM(NSInteger, STKAudioPlayerErrorCode)
{
STKAudioPlayerErrorNone = 0,
STKAudioPlayerErrorDataSource,
@@ -77,9 +75,13 @@ typedef enum
STKAudioPlayerErrorCodecError,
STKAudioPlayerErrorDataNotFound,
STKAudioPlayerErrorOther = 0xffff
}
STKAudioPlayerErrorCode;
};
///
/// Options to initiailise the Audioplayer with.
/// By default if you set buffer size or seconds to 0, the non-zero default will be used
/// If you would like to disable the buffer option completely set to STK_DISABLE_BUFFER
///
typedef struct
{
/// If YES then seeking a track will cause all pending items to be flushed from the queue
@@ -91,7 +93,7 @@ typedef struct
/// 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;
Float32 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)
@@ -101,6 +103,8 @@ typedef struct
}
STKAudioPlayerOptions;
#define STK_DISABLE_BUFFER (0xffffffff)
typedef void(^STKFrameFilter)(UInt32 channelsPerFrame, UInt32 bytesPerFrame, UInt32 frameCount, void* frames);
@interface STKFrameFilterEntry : NSObject
+286 -6
View File
@@ -63,6 +63,9 @@
#define STK_DEFAULT_PACKET_BUFFER_SIZE (2048)
#define STK_DEFAULT_GRACE_PERIOD_AFTER_SEEK_SECONDS (0.5)
#define OSSTATUS_PRINTF_PLACEHOLDER @"%c%c%c%c"
#define OSSTATUS_PRINTF_VALUE(status) (char)(((status) >> 24) & 0xFF), (char)(((status) >> 16) & 0xFF), (char)(((status) >> 8) & 0xFF), (char)((status) & 0xFF)
#define LOGINFO(x) [self logInfo:[NSString stringWithFormat:@"%s %@", sel_getName(_cmd), x]];
static void PopulateOptionsWithDefault(STKAudioPlayerOptions* options)
@@ -93,6 +96,34 @@ static void PopulateOptionsWithDefault(STKAudioPlayerOptions* options)
}
}
static void NormalizeDisabledBuffers(STKAudioPlayerOptions* options)
{
if (options->bufferSizeInSeconds == STK_DISABLE_BUFFER)
{
options->bufferSizeInSeconds = 0;
}
if (options->readBufferSize == STK_DISABLE_BUFFER)
{
options->readBufferSize = 0;
}
if (options->secondsRequiredToStartPlaying == STK_DISABLE_BUFFER)
{
options->secondsRequiredToStartPlaying = 0;
}
if (options->secondsRequiredToStartPlayingAfterBufferUnderun == STK_DISABLE_BUFFER)
{
options->secondsRequiredToStartPlayingAfterBufferUnderun = 0;
}
if (options->gracePeriodAfterSeekInSeconds == STK_DISABLE_BUFFER)
{
options->gracePeriodAfterSeekInSeconds = 0;
}
}
#define CHECK_STATUS_AND_REPORT(call) \
if ((status = (call))) \
{ \
@@ -173,6 +204,7 @@ static AudioComponentDescription nbandUnitDescription;
static AudioComponentDescription outputUnitDescription;
static AudioComponentDescription convertUnitDescription;
static AudioStreamBasicDescription canonicalAudioStreamBasicDescription;
static AudioStreamBasicDescription recordAudioStreamBasicDescription;
@interface STKAudioPlayer()
{
@@ -241,7 +273,16 @@ static AudioStreamBasicDescription canonicalAudioStreamBasicDescription;
AudioFileStreamID audioFileStream;
NSConditionLock* threadStartedLock;
NSConditionLock* threadFinishedCondLock;
AudioFileID recordAudioFileId;
UInt32 recordFilePacketPosition;
AudioConverterRef recordAudioConverterRef;
UInt32 recordOutputBufferSize;
UInt8 *recordOutputBuffer;
UInt32 recordPacketsPerBuffer;
UInt32 recordPacketSize;
AudioStreamPacketDescription *recordPacketDescriptions;
void(^stopBackBackgroundTaskBlock)();
int32_t seekVersion;
@@ -292,9 +333,15 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
.componentSubType = kAudioUnitSubType_AUConverter,
.componentFlags = 0,
.componentFlagsMask = 0
};
};
const int bytesPerSample = 2;
#ifdef CA_CANONICAL_DEPRECATED
const int bytesPerSample = sizeof(SInt16);
#elif __IPHONE_OS_VERSION_MIN_REQUIRED >= 80000
const int bytesPerSample = sizeof(SInt16);
#else
const int bytesPerSample = sizeof(AudioSampleType);
#endif
canonicalAudioStreamBasicDescription = (AudioStreamBasicDescription)
{
@@ -307,7 +354,7 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
.mBitsPerChannel = 8 * bytesPerSample,
.mBytesPerPacket = (bytesPerSample * 2)
};
outputUnitDescription = (AudioComponentDescription)
{
.componentType = kAudioUnitType_Output,
@@ -472,6 +519,7 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
self->equalizerEnabled = optionsIn.equalizerBandFrequencies[0] != 0;
PopulateOptionsWithDefault(&options);
NormalizeDisabledBuffers(&options);
framesRequiredToStartPlaying = canonicalAudioStreamBasicDescription.mSampleRate * options.secondsRequiredToStartPlaying;
framesRequiredToPlayAfterRebuffering = canonicalAudioStreamBasicDescription.mSampleRate * options.secondsRequiredToStartPlayingAfterBufferUnderun;
@@ -538,6 +586,8 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
OSSpinLockUnlock(&currentEntryReferencesLock);
}
[self closeRecordAudioFile];
[self stopAudioUnitWithReason:STKAudioPlayerStopReasonDisposed];
[self clearQueue];
@@ -578,6 +628,7 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
pthread_cond_destroy(&mainThreadSyncCallReadyCondition);
free(readBuffer);
free(pcmAudioBufferList.mBuffers[0].mData);
}
-(void) startSystemBackgroundTask
@@ -856,7 +907,7 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
}
case kAudioFileStreamProperty_ReadyToProducePackets:
{
if (!audioConverterAudioStreamBasicDescription.mFormatID == kAudioFormatLinearPCM)
if (audioConverterAudioStreamBasicDescription.mFormatID != kAudioFormatLinearPCM)
{
discontinuous = YES;
}
@@ -1095,6 +1146,8 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
[currentlyReadingEntry.dataSource registerForEvents:[NSRunLoop currentRunLoop]];
[currentlyReadingEntry.dataSource seekToOffset:0];
[self closeRecordAudioFile];
if (startPlaying)
{
if (clearQueue)
@@ -1397,6 +1450,8 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
currentlyReadingEntry = nil;
OSSpinLockUnlock(&currentEntryReferencesLock);
pthread_mutex_unlock(&playerMutex);
[self closeRecordAudioFile];
self.internalState = STKAudioPlayerInternalStateDisposed;
@@ -1459,6 +1514,11 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
AudioConverterReset(audioConverterRef);
}
if (recordAudioConverterRef)
{
AudioConverterReset(recordAudioConverterRef);
}
[currentEntry reset];
[currentEntry.dataSource seekToOffset:seekByteOffset];
@@ -1577,7 +1637,9 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
}
NSObject* queueItemId = currentlyReadingEntry.queueItemId;
[self closeRecordAudioFile];
[self dispatchSyncOnMainThread:^
{
[self.delegate audioPlayer:self didFinishBufferingSourceWithQueueItemId:queueItemId];
@@ -1702,6 +1764,8 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
return;
}
[self closeRecordAudioFile];
[self stopAudioUnitWithReason:STKAudioPlayerStopReasonUserAction];
[self resetPcmBuffers];
@@ -1796,6 +1860,35 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
self.muted = NO;
}
-(void) closeRecordAudioFile
{
if (recordAudioFileId)
{
AudioFileClose(recordAudioFileId);
recordAudioFileId = NULL;
}
if (recordAudioConverterRef)
{
AudioConverterDispose(recordAudioConverterRef);
recordAudioConverterRef = nil;
}
if (recordOutputBuffer)
{
free(recordOutputBuffer);
recordOutputBuffer = NULL;
}
if (recordPacketDescriptions)
{
free(recordPacketDescriptions);
recordPacketDescriptions = NULL;
}
recordFilePacketPosition = 0;
}
-(void) dispose
{
[self stop];
@@ -1874,11 +1967,37 @@ static BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* cl
{
AudioConverterReset(audioConverterRef);
if (recordAudioConverterRef)
{
AudioConverterReset(recordAudioConverterRef);
}
return;
}
[self destroyAudioConverter];
canonicalAudioStreamBasicDescription.mChannelsPerFrame = asbd->mChannelsPerFrame;
BOOL isRecording = currentlyReadingEntry.dataSource.recordToFileUrl != nil;
if (isRecording)
{
recordAudioStreamBasicDescription = (AudioStreamBasicDescription)
{
.mFormatID = kAudioFormatMPEG4AAC,
.mFormatFlags = kMPEG4Object_AAC_LC,
.mChannelsPerFrame = canonicalAudioStreamBasicDescription.mChannelsPerFrame,
.mSampleRate = canonicalAudioStreamBasicDescription.mSampleRate,
};
UInt32 dataSize = sizeof(recordAudioStreamBasicDescription);
AudioFormatGetProperty(kAudioFormatProperty_FormatInfo,
0,
NULL,
&dataSize,
&recordAudioStreamBasicDescription);
}
AudioClassDescription classDesc;
if (GetHardwareCodecClassDesc(asbd->mFormatID, &classDesc))
@@ -1897,6 +2016,16 @@ static BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* cl
return;
}
}
if (isRecording && !recordAudioConverterRef)
{
status = AudioConverterNew(&canonicalAudioStreamBasicDescription, &recordAudioStreamBasicDescription, &recordAudioConverterRef);
if (status)
{
NSLog(@"STKAudioPlayer failed to create a recording audio converter");
}
}
audioConverterAudioStreamBasicDescription = *asbd;
@@ -1927,6 +2056,82 @@ static BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* cl
return;
}
}
if (recordAudioConverterRef)
{
if (recordAudioFileId)
{
AudioFileClose(recordAudioFileId);
recordAudioFileId = NULL;
}
if (recordOutputBuffer)
{
free(recordOutputBuffer);
recordOutputBuffer = NULL;
}
if (recordPacketDescriptions)
{
free(recordPacketDescriptions);
recordPacketDescriptions = NULL;
}
recordOutputBufferSize = 32 * 1024;
recordPacketSize = canonicalAudioStreamBasicDescription.mBytesPerPacket;
if (recordPacketSize == 0)
{
UInt32 size = sizeof(recordPacketSize);
if (0 == AudioConverterGetProperty(recordAudioConverterRef, kAudioConverterPropertyMaximumOutputPacketSize, &size, &recordPacketSize))
{
if (recordPacketSize > recordOutputBufferSize)
{
recordOutputBufferSize = recordPacketSize;
}
recordPacketsPerBuffer = recordOutputBufferSize / recordPacketSize;
}
else
{
AudioConverterDispose(recordAudioConverterRef);
recordAudioConverterRef = NULL;
NSLog(@"STKAudioPlayer: Can't support this output format for recording");
}
}
else
{
recordPacketsPerBuffer = recordOutputBufferSize / recordPacketSize;
}
UInt32 propertySize = sizeof(UInt32);
UInt32 externallyFramed = 0;
OSStatus error = AudioFormatGetProperty(kAudioFormatProperty_FormatIsExternallyFramed, sizeof(recordAudioStreamBasicDescription), &recordAudioStreamBasicDescription, &propertySize, &externallyFramed);
if (externallyFramed)
{
recordPacketDescriptions = (AudioStreamPacketDescription *)malloc(sizeof(AudioStreamPacketDescription) * recordPacketsPerBuffer);
}
recordOutputBuffer = (UInt8 *)malloc(sizeof(UInt8) * recordOutputBufferSize);
error = AudioFileCreateWithURL(
(__bridge CFURLRef)(currentlyReadingEntry.dataSource.recordToFileUrl),
kAudioFileCAFType,
&recordAudioStreamBasicDescription,
kAudioFileFlags_EraseFile,
&recordAudioFileId);
recordFilePacketPosition = 0;
if (error)
{
NSLog(@"STKAudioPlayer failed to create a recording audio file at %@", currentlyReadingEntry.dataSource.recordToFileUrl);
[self closeRecordAudioFile];
}
}
}
-(void) createOutputUnit
@@ -2418,6 +2623,11 @@ OSStatus AudioConverterCallback(AudioConverterRef inAudioConverter, UInt32* ioNu
status = AudioConverterFillComplexBuffer(audioConverterRef, AudioConverterCallback, (void*)&convertInfo, &framesToDecode, &localPcmBufferList, NULL);
framesAdded = framesToDecode;
if ((status == 100 || status == 0) && recordAudioFileId && recordAudioConverterRef)
{
[self handleRecordingOfAudioPackets:framesToDecode audioBuffer:&localPcmBufferList.mBuffers[0]];
}
if (status == 100)
{
@@ -2461,6 +2671,11 @@ OSStatus AudioConverterCallback(AudioConverterRef inAudioConverter, UInt32* ioNu
framesAdded += framesToDecode;
if ((status == 100 || status == 0) && recordAudioFileId && recordAudioConverterRef)
{
[self handleRecordingOfAudioPackets:framesToDecode audioBuffer:&localPcmBufferList.mBuffers[0]];
}
if (status == 100)
{
OSSpinLockLock(&pcmBufferSpinLock);
@@ -2505,6 +2720,11 @@ OSStatus AudioConverterCallback(AudioConverterRef inAudioConverter, UInt32* ioNu
framesAdded = framesToDecode;
if ((status == 100 || status == 0) && recordAudioFileId && recordAudioConverterRef)
{
[self handleRecordingOfAudioPackets:framesToDecode audioBuffer:&localPcmBufferList.mBuffers[0]];
}
if (status == 100)
{
OSSpinLockLock(&pcmBufferSpinLock);
@@ -2539,6 +2759,66 @@ OSStatus AudioConverterCallback(AudioConverterRef inAudioConverter, UInt32* ioNu
}
}
- (void)handleRecordingOfAudioPackets:(UInt32)numberOfPackets audioBuffer:(AudioBuffer *)audioBuffer
{
if (recordAudioFileId && recordAudioConverterRef)
{
AudioConvertInfo recordConvertInfo;
recordConvertInfo.done = NO;
recordConvertInfo.numberOfPackets = numberOfPackets;
recordConvertInfo.packetDescriptions = NULL;
recordConvertInfo.audioBuffer = *audioBuffer;
AudioBufferList convertedData;
convertedData.mNumberBuffers = 1;
convertedData.mBuffers[0].mNumberChannels = recordAudioStreamBasicDescription.mChannelsPerFrame;
convertedData.mBuffers[0].mDataByteSize = recordOutputBufferSize;
convertedData.mBuffers[0].mData = recordOutputBuffer;
UInt32 ioOutputDataPackets;
OSStatus status;
while (1)
{
ioOutputDataPackets = recordPacketsPerBuffer;
status = AudioConverterFillComplexBuffer(recordAudioConverterRef, AudioConverterCallback, (void*)&recordConvertInfo, &ioOutputDataPackets, &convertedData, recordPacketDescriptions);
if (status == 100 || status == 0)
{
if (ioOutputDataPackets > 0)
{
OSStatus writeError = AudioFileWritePackets(recordAudioFileId,
NO,
convertedData.mBuffers[0].mDataByteSize,
recordPacketDescriptions,
recordFilePacketPosition,
&ioOutputDataPackets,
convertedData.mBuffers[0].mData);
if (writeError)
{
NSLog(@"STKAudioPlayer:handleRecordingOfAudioPackets failed on AudioFileWritePackets with error \"" OSSTATUS_PRINTF_PLACEHOLDER "\"", OSSTATUS_PRINTF_VALUE(writeError));
}
else
{
recordFilePacketPosition += ioOutputDataPackets;
}
}
}
else
{
NSLog(@"STKAudioPlayer: Unexpected error during recording audio file conversion");
}
if (status == 100)
{
break;
}
}
}
}
static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData)
{
STKAudioPlayer* audioPlayer = (__bridge STKAudioPlayer*)inRefCon;
@@ -101,6 +101,8 @@ static void PopulateOptionsWithDefault(STKAutoRecoveringHTTPDataSourceOptions* o
@implementation STKAutoRecoveringHTTPDataSource
@dynamic innerDataSource;
-(STKHTTPDataSource*) innerHTTPDataSource
{
return (STKHTTPDataSource*)self.innerDataSource;
@@ -137,8 +137,6 @@ static void ReadStreamCallbackProc(CFReadStreamRef stream, CFStreamEventType eve
{
CFReadStreamSetClient(stream, kCFStreamEventHasBytesAvailable | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered, NULL, NULL);
CFReadStreamUnscheduleFromRunLoop(stream, [eventsRunLoop getCFRunLoop], kCFRunLoopCommonModes);
eventsRunLoop = nil;
}
}
+2
View File
@@ -49,7 +49,9 @@
@property (readonly) SInt64 position;
@property (readonly) SInt64 length;
@property (readonly) BOOL hasBytesAvailable;
@property (nonatomic, readwrite, assign) double durationHint;
@property (readwrite, unsafe_unretained) id<STKDataSourceDelegate> delegate;
@property (nonatomic, strong) NSURL *recordToFileUrl;
-(BOOL) registerForEvents:(NSRunLoop*)runLoop;
-(void) unregisterForEvents;
+60 -57
View File
@@ -164,6 +164,50 @@
return audioFileTypeHint;
}
-(NSDictionary*) parseIceHeader:(NSData*)headerData
{
NSMutableDictionary* retval = [[NSMutableDictionary alloc] init];
NSCharacterSet* characterSet = [NSCharacterSet characterSetWithCharactersInString:@"\r\n"];
NSString* fullString = [[NSString alloc] initWithData:headerData encoding:NSUTF8StringEncoding];
NSArray* strings = [fullString componentsSeparatedByCharactersInSet:characterSet];
httpHeaders = [NSMutableDictionary dictionary];
for (NSString* s in strings)
{
if (s.length == 0)
{
continue;
}
if ([s hasPrefix:@"ICY "])
{
NSArray* parts = [s componentsSeparatedByString:@" "];
if (parts.count >= 2)
{
self->httpStatusCode = [parts[1] intValue];
}
continue;
}
NSRange range = [s rangeOfString:@":"];
if (range.location == NSNotFound)
{
continue;
}
NSString* key = [s substringWithRange: (NSRange){.location = 0, .length = range.location}];
NSString* value = [s substringFromIndex:range.location + 1];
[retval setValue:value forKey:key];
}
return retval;
}
-(BOOL) parseHttpHeader
{
if (!httpHeaderNotAvailable)
@@ -197,7 +241,6 @@
if (!self->iceHeaderSearchComplete)
{
UInt8 byte;
UInt8 bytes[4];
UInt8 terminal1[] = { '\n', '\n' };
UInt8 terminal2[] = { '\r', '\n', '\r', '\n' };
@@ -224,9 +267,7 @@
if (iceHeaderData.length >= sizeof(terminal1))
{
[iceHeaderData getBytes:&bytes[0] range:(NSRange){.location = iceHeaderData.length - sizeof(terminal1), .length = sizeof(terminal1)}];
if (memcmp(&terminal1[0], &bytes[0], sizeof(terminal1)) == 0)
if (memcmp(&terminal1[0], [self->iceHeaderData bytes] + iceHeaderData.length - sizeof(terminal1), sizeof(terminal1)) == 0)
{
self->iceHeaderAvailable = YES;
self->iceHeaderSearchComplete = YES;
@@ -237,9 +278,7 @@
if (iceHeaderData.length >= sizeof(terminal2))
{
[iceHeaderData getBytes:&bytes[0] range:(NSRange){.location = iceHeaderData.length - sizeof(terminal2), .length = sizeof(terminal2)}];
if (memcmp(&terminal2[0], &bytes[0], sizeof(terminal2)) == 0)
if (memcmp(&terminal2[0], [self->iceHeaderData bytes] + iceHeaderData.length - sizeof(terminal2), sizeof(terminal2)) == 0)
{
self->iceHeaderAvailable = YES;
self->iceHeaderSearchComplete = YES;
@@ -248,11 +287,9 @@
}
}
if (iceHeaderData.length >=4)
if (iceHeaderData.length >= 4)
{
[iceHeaderData getBytes:&bytes[0] length:4];
if (memcmp(bytes, "ICY", 3) != 0 && memcmp(bytes, "HTTP", 4) != 0)
if (memcmp([self->iceHeaderData bytes], "ICY ", 4) != 0 && memcmp([self->iceHeaderData bytes], "HTTP", 4) != 0)
{
self->iceHeaderAvailable = NO;
self->iceHeaderSearchComplete = YES;
@@ -269,45 +306,12 @@
}
}
NSCharacterSet* characterSet = [NSCharacterSet characterSetWithCharactersInString:@"\r\n"];
NSString* fullString = [[NSString alloc] initWithData:self->iceHeaderData encoding:NSUTF8StringEncoding];
httpHeaders = [self parseIceHeader:self->iceHeaderData];
NSArray* strings = [fullString componentsSeparatedByCharactersInSet:characterSet];
httpHeaders = [NSMutableDictionary dictionary];
for (NSString* s in strings)
{
if (s.length == 0)
{
continue;
}
if ([s hasPrefix:@"ICY"])
{
NSArray* ss = [s componentsSeparatedByString:@" "];
if (ss.count >= 2)
{
self->httpStatusCode = [ss[1] intValue];
}
}
NSRange range = [s rangeOfString:@":"];
if (range.location == NSNotFound)
{
continue;
}
NSString* key = [s substringWithRange: (NSRange){.location = 0, .length = range.location}];
NSString* value = [s substringFromIndex:range.location + 1];
[httpHeaders setValue:value forKey:key];
}
self->iceHeaderData = nil;
}
if (([httpHeaders objectForKey:@"Accepts-Ranges"] ?: [httpHeaders objectForKey:@"accepts-ranges"]) != nil)
if (([httpHeaders objectForKey:@"Accept-Ranges"] ?: [httpHeaders objectForKey:@"accept-ranges"]) != nil)
{
self->supportsSeek = YES;
}
@@ -322,7 +326,6 @@
}
NSString* contentType = [httpHeaders objectForKey:@"Content-Type"] ?: [httpHeaders objectForKey:@"content-type"] ;
AudioFileTypeID typeIdFromMimeType = [STKHTTPDataSource audioFileTypeHintFromMimeType:contentType];
if (typeIdFromMimeType != 0)
@@ -436,6 +439,11 @@
}
-(int) readIntoBuffer:(UInt8*)buffer withSize:(int)size
{
return [self privateReadIntoBuffer:buffer withSize:size];
}
-(int) privateReadIntoBuffer:(UInt8*)buffer withSize:(int)size
{
if (size == 0)
{
@@ -458,8 +466,7 @@
return count;
}
int read = (int)CFReadStreamRead(stream, buffer, size);
int read = [super readIntoBuffer:buffer withSize:size];
if (read < 0)
{
@@ -490,8 +497,6 @@
return;
}
self->supportsSeek = NO;
self->currentUrl = url;
if (url == nil)
@@ -511,11 +516,12 @@
for (NSString* key in self->requestHeaders)
{
NSString* value = [self->requestHeaders objectForKey:key];
CFHTTPMessageSetHeaderFieldValue(message, (__bridge CFStringRef)key, (__bridge CFStringRef)value);
}
CFHTTPMessageSetHeaderFieldValue(message, (__bridge CFStringRef)@"Accept", (__bridge CFStringRef)@"*/*");
CFHTTPMessageSetHeaderFieldValue(message, (__bridge CFStringRef)@"Ice-MetaData", (__bridge CFStringRef)@"0");
CFHTTPMessageSetHeaderFieldValue(message, CFSTR("Accept"), CFSTR("*/*"));
CFHTTPMessageSetHeaderFieldValue(message, CFSTR("Ice-MetaData"), CFSTR("0"));
stream = CFReadStreamCreateForHTTPRequest(NULL, message);
@@ -540,13 +546,11 @@
}
// Proxy support
CFDictionaryRef proxySettings = CFNetworkCopySystemProxySettings();
CFReadStreamSetProperty(stream, kCFStreamPropertyHTTPProxy, proxySettings);
CFRelease(proxySettings);
// SSL support
if ([self->currentUrl.scheme caseInsensitiveCompare:@"https"] == NSOrderedSame)
{
NSDictionary* sslSettings = [NSDictionary dictionaryWithObjectsAndKeys:
@@ -563,7 +567,6 @@
self->httpStatusCode = 0;
// Open
if (!CFReadStreamOpen(stream))
{
CFRelease(stream);
+1
View File
@@ -28,6 +28,7 @@
volatile int processedPacketsCount;
volatile int processedPacketsSizeTotal;
AudioStreamBasicDescription audioStreamBasicDescription;
double durationHint;
}
@property (readonly) UInt64 audioDataLengthInBytes;
+4 -1
View File
@@ -23,6 +23,7 @@
self.dataSource = dataSourceIn;
self.queueItemId = queueItemIdIn;
self->lastFrameQueued = -1;
self->durationHint = dataSourceIn.durationHint;
}
return self;
@@ -45,7 +46,7 @@
{
if (processedPacketsCount > STK_BIT_RATE_ESTIMATION_MIN_PACKETS_PREFERRED || (audioStreamBasicDescription.mBytesPerFrame == 0 && processedPacketsCount > STK_BIT_RATE_ESTIMATION_MIN_PACKETS_MIN))
{
double averagePacketByteSize = processedPacketsSizeTotal / processedPacketsCount;
double averagePacketByteSize = (double)processedPacketsSizeTotal / (double)processedPacketsCount;
retval = averagePacketByteSize / packetDuration * 8;
@@ -60,6 +61,8 @@
-(double) duration
{
if (durationHint > 0.0) return durationHint;
if (self->sampleRate <= 0)
{
return 0;
@@ -7,7 +7,7 @@
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>com.abstractpath.${PRODUCT_NAME:rfc1034identifier}</string>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
@@ -7,7 +7,7 @@
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>abstractpath.com.${PRODUCT_NAME:rfc1034identifier}</string>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>