Initial commit
This commit is contained in:
+74
@@ -0,0 +1,74 @@
|
||||
.vagrant
|
||||
Berksfile.lock
|
||||
*~
|
||||
*#
|
||||
.#*
|
||||
\#*#
|
||||
.*.sw[a-z]
|
||||
*.un~
|
||||
/cookbooks
|
||||
*.versionsBackup
|
||||
*.orig
|
||||
*.log
|
||||
.svn/
|
||||
|
||||
# Bundler
|
||||
Gemfile.lock
|
||||
bin/*
|
||||
.bundle/*
|
||||
|
||||
# Compiled source #
|
||||
###################
|
||||
*.com
|
||||
*.class
|
||||
*.dll
|
||||
*.exe
|
||||
*.o
|
||||
*.so
|
||||
*.a
|
||||
*.pyc
|
||||
|
||||
# OS generated files #
|
||||
######################
|
||||
.DS_Store
|
||||
.DS_Store?
|
||||
._*
|
||||
.Spotlight-V100
|
||||
.Trashes
|
||||
Icon?
|
||||
ehthumbs.db
|
||||
Thumbs.db
|
||||
metafortress.dat
|
||||
|
||||
# BlackBerry generated files #
|
||||
######################
|
||||
arm/
|
||||
x86/
|
||||
.preview*
|
||||
*.qm
|
||||
|
||||
# Per Project Exclusions #
|
||||
######################
|
||||
|
||||
/.travis.yml
|
||||
attributes/.travis.yml
|
||||
definitions/.travis.yml
|
||||
files/.travis.yml
|
||||
files/default/.travis.yml
|
||||
libraries/.travis.yml
|
||||
providers/.travis.yml
|
||||
recipes/.travis.yml
|
||||
resources/.travis.yml
|
||||
templates/.travis.yml
|
||||
templates/default/.travis.yml
|
||||
.buildpath
|
||||
.project
|
||||
.chefignore
|
||||
.settings
|
||||
.idea
|
||||
|
||||
# XCode Related
|
||||
################
|
||||
xcuserdata/
|
||||
xcshareddata/
|
||||
|
||||
@@ -0,0 +1,429 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 46;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
04F4C41F1E301BCE0063E88E /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 04F4C41E1E301BCE0063E88E /* main.m */; };
|
||||
04F4C4221E301BCE0063E88E /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 04F4C4211E301BCE0063E88E /* AppDelegate.m */; };
|
||||
04F4C4251E301BCE0063E88E /* PlaybackHeadersViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 04F4C4241E301BCE0063E88E /* PlaybackHeadersViewController.m */; };
|
||||
04F4C4281E301BCE0063E88E /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 04F4C4261E301BCE0063E88E /* Main.storyboard */; };
|
||||
04F4C42A1E301BCE0063E88E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 04F4C4291E301BCE0063E88E /* Assets.xcassets */; };
|
||||
04F4C42D1E301BCE0063E88E /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 04F4C42B1E301BCE0063E88E /* LaunchScreen.storyboard */; };
|
||||
04F4C4361E301CA80063E88E /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 04F4C4351E301CA80063E88E /* AVFoundation.framework */; };
|
||||
04F4C4391E301CC90063E88E /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 04F4C4371E301CC90063E88E /* CoreFoundation.framework */; };
|
||||
04F4C43A1E301CC90063E88E /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 04F4C4381E301CC90063E88E /* CoreGraphics.framework */; };
|
||||
04F4C43C1E301CD00063E88E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 04F4C43B1E301CD00063E88E /* Foundation.framework */; };
|
||||
04F4C43E1E301CD80063E88E /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 04F4C43D1E301CD80063E88E /* UIKit.framework */; };
|
||||
04F4C4401E301CF10063E88E /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 04F4C43F1E301CF10063E88E /* libz.tbd */; };
|
||||
04F4C4431E3020A80063E88E /* AVPlayerReverseProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 04F4C4421E3020A80063E88E /* AVPlayerReverseProxy.m */; };
|
||||
0D5D1DCBB09E7F91417EB499 /* libPods-AVPlayerHTTPHeaders.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C39FE6AFC35E44B68C25030 /* libPods-AVPlayerHTTPHeaders.a */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
04F4C41A1E301BCE0063E88E /* AVPlayerHTTPHeaders.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AVPlayerHTTPHeaders.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
04F4C41E1E301BCE0063E88E /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
|
||||
04F4C4201E301BCE0063E88E /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
|
||||
04F4C4211E301BCE0063E88E /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
|
||||
04F4C4231E301BCE0063E88E /* PlaybackHeadersViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PlaybackHeadersViewController.h; sourceTree = "<group>"; };
|
||||
04F4C4241E301BCE0063E88E /* PlaybackHeadersViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PlaybackHeadersViewController.m; sourceTree = "<group>"; };
|
||||
04F4C4271E301BCE0063E88E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
||||
04F4C4291E301BCE0063E88E /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
04F4C42C1E301BCE0063E88E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||
04F4C42E1E301BCE0063E88E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
04F4C4351E301CA80063E88E /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; };
|
||||
04F4C4371E301CC90063E88E /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; };
|
||||
04F4C4381E301CC90063E88E /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
|
||||
04F4C43B1E301CD00063E88E /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
|
||||
04F4C43D1E301CD80063E88E /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
|
||||
04F4C43F1E301CF10063E88E /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; };
|
||||
04F4C4411E3020A80063E88E /* AVPlayerReverseProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AVPlayerReverseProxy.h; sourceTree = "<group>"; };
|
||||
04F4C4421E3020A80063E88E /* AVPlayerReverseProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AVPlayerReverseProxy.m; sourceTree = "<group>"; };
|
||||
17CE32C9C8C4ABF27FB5A7CD /* Pods-AVPlayerHTTPHeaders.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AVPlayerHTTPHeaders.release.xcconfig"; path = "Pods/Target Support Files/Pods-AVPlayerHTTPHeaders/Pods-AVPlayerHTTPHeaders.release.xcconfig"; sourceTree = "<group>"; };
|
||||
4C39FE6AFC35E44B68C25030 /* libPods-AVPlayerHTTPHeaders.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-AVPlayerHTTPHeaders.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
F44A8BB4647C2FC277E40667 /* Pods-AVPlayerHTTPHeaders.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AVPlayerHTTPHeaders.debug.xcconfig"; path = "Pods/Target Support Files/Pods-AVPlayerHTTPHeaders/Pods-AVPlayerHTTPHeaders.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
04F4C4171E301BCE0063E88E /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
04F4C4401E301CF10063E88E /* libz.tbd in Frameworks */,
|
||||
04F4C43E1E301CD80063E88E /* UIKit.framework in Frameworks */,
|
||||
04F4C43C1E301CD00063E88E /* Foundation.framework in Frameworks */,
|
||||
04F4C4391E301CC90063E88E /* CoreFoundation.framework in Frameworks */,
|
||||
04F4C43A1E301CC90063E88E /* CoreGraphics.framework in Frameworks */,
|
||||
04F4C4361E301CA80063E88E /* AVFoundation.framework in Frameworks */,
|
||||
0D5D1DCBB09E7F91417EB499 /* libPods-AVPlayerHTTPHeaders.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
04F4C4111E301BCE0063E88E = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
04F4C41C1E301BCE0063E88E /* AVPlayerHTTPHeaders */,
|
||||
04F4C41B1E301BCE0063E88E /* Products */,
|
||||
04F4C4341E301CA80063E88E /* Frameworks */,
|
||||
929398F0CD2BC0CBBE4D9904 /* Pods */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
04F4C41B1E301BCE0063E88E /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
04F4C41A1E301BCE0063E88E /* AVPlayerHTTPHeaders.app */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
04F4C41C1E301BCE0063E88E /* AVPlayerHTTPHeaders */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
04F4C4411E3020A80063E88E /* AVPlayerReverseProxy.h */,
|
||||
04F4C4421E3020A80063E88E /* AVPlayerReverseProxy.m */,
|
||||
04F4C4231E301BCE0063E88E /* PlaybackHeadersViewController.h */,
|
||||
04F4C4241E301BCE0063E88E /* PlaybackHeadersViewController.m */,
|
||||
04F4C4201E301BCE0063E88E /* AppDelegate.h */,
|
||||
04F4C4211E301BCE0063E88E /* AppDelegate.m */,
|
||||
04F4C4261E301BCE0063E88E /* Main.storyboard */,
|
||||
04F4C4291E301BCE0063E88E /* Assets.xcassets */,
|
||||
04F4C42B1E301BCE0063E88E /* LaunchScreen.storyboard */,
|
||||
04F4C42E1E301BCE0063E88E /* Info.plist */,
|
||||
04F4C41D1E301BCE0063E88E /* Supporting Files */,
|
||||
);
|
||||
path = AVPlayerHTTPHeaders;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
04F4C41D1E301BCE0063E88E /* Supporting Files */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
04F4C41E1E301BCE0063E88E /* main.m */,
|
||||
);
|
||||
name = "Supporting Files";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
04F4C4341E301CA80063E88E /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
04F4C43F1E301CF10063E88E /* libz.tbd */,
|
||||
04F4C43D1E301CD80063E88E /* UIKit.framework */,
|
||||
04F4C43B1E301CD00063E88E /* Foundation.framework */,
|
||||
04F4C4371E301CC90063E88E /* CoreFoundation.framework */,
|
||||
04F4C4381E301CC90063E88E /* CoreGraphics.framework */,
|
||||
04F4C4351E301CA80063E88E /* AVFoundation.framework */,
|
||||
4C39FE6AFC35E44B68C25030 /* libPods-AVPlayerHTTPHeaders.a */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
929398F0CD2BC0CBBE4D9904 /* Pods */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F44A8BB4647C2FC277E40667 /* Pods-AVPlayerHTTPHeaders.debug.xcconfig */,
|
||||
17CE32C9C8C4ABF27FB5A7CD /* Pods-AVPlayerHTTPHeaders.release.xcconfig */,
|
||||
);
|
||||
name = Pods;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
04F4C4191E301BCE0063E88E /* AVPlayerHTTPHeaders */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 04F4C4311E301BCE0063E88E /* Build configuration list for PBXNativeTarget "AVPlayerHTTPHeaders" */;
|
||||
buildPhases = (
|
||||
EC4CDE8692B8F14736D281EB /* [CP] Check Pods Manifest.lock */,
|
||||
04F4C4161E301BCE0063E88E /* Sources */,
|
||||
04F4C4171E301BCE0063E88E /* Frameworks */,
|
||||
04F4C4181E301BCE0063E88E /* Resources */,
|
||||
3BAA9258018FA30D7A569334 /* [CP] Embed Pods Frameworks */,
|
||||
E3A8378BACD0CE34F1AFB448 /* [CP] Copy Pods Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = AVPlayerHTTPHeaders;
|
||||
productName = AVPlayerHTTPHeaders;
|
||||
productReference = 04F4C41A1E301BCE0063E88E /* AVPlayerHTTPHeaders.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
04F4C4121E301BCE0063E88E /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 0820;
|
||||
ORGANIZATIONNAME = "Prophet Studios";
|
||||
TargetAttributes = {
|
||||
04F4C4191E301BCE0063E88E = {
|
||||
CreatedOnToolsVersion = 8.2.1;
|
||||
DevelopmentTeam = V8SSGR3H48;
|
||||
ProvisioningStyle = Automatic;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 04F4C4151E301BCE0063E88E /* Build configuration list for PBXProject "AVPlayerHTTPHeaders" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
developmentRegion = English;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = 04F4C4111E301BCE0063E88E;
|
||||
productRefGroup = 04F4C41B1E301BCE0063E88E /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
04F4C4191E301BCE0063E88E /* AVPlayerHTTPHeaders */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
04F4C4181E301BCE0063E88E /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
04F4C42D1E301BCE0063E88E /* LaunchScreen.storyboard in Resources */,
|
||||
04F4C42A1E301BCE0063E88E /* Assets.xcassets in Resources */,
|
||||
04F4C4281E301BCE0063E88E /* Main.storyboard in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
3BAA9258018FA30D7A569334 /* [CP] Embed Pods Frameworks */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "[CP] Embed Pods Frameworks";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-AVPlayerHTTPHeaders/Pods-AVPlayerHTTPHeaders-frameworks.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
E3A8378BACD0CE34F1AFB448 /* [CP] Copy Pods Resources */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "[CP] Copy Pods Resources";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-AVPlayerHTTPHeaders/Pods-AVPlayerHTTPHeaders-resources.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
EC4CDE8692B8F14736D281EB /* [CP] Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "[CP] Check Pods Manifest.lock";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
04F4C4161E301BCE0063E88E /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
04F4C4251E301BCE0063E88E /* PlaybackHeadersViewController.m in Sources */,
|
||||
04F4C4221E301BCE0063E88E /* AppDelegate.m in Sources */,
|
||||
04F4C4431E3020A80063E88E /* AVPlayerReverseProxy.m in Sources */,
|
||||
04F4C41F1E301BCE0063E88E /* main.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
04F4C4261E301BCE0063E88E /* Main.storyboard */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
04F4C4271E301BCE0063E88E /* Base */,
|
||||
);
|
||||
name = Main.storyboard;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
04F4C42B1E301BCE0063E88E /* LaunchScreen.storyboard */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
04F4C42C1E301BCE0063E88E /* Base */,
|
||||
);
|
||||
name = LaunchScreen.storyboard;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXVariantGroup section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
04F4C42F1E301BCE0063E88E /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 10.2;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
04F4C4301E301BCE0063E88E /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 10.2;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
04F4C4321E301BCE0063E88E /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = F44A8BB4647C2FC277E40667 /* Pods-AVPlayerHTTPHeaders.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
DEVELOPMENT_TEAM = V8SSGR3H48;
|
||||
INFOPLIST_FILE = AVPlayerHTTPHeaders/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.prophetstudios.AVPlayerHTTPHeaders;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
04F4C4331E301BCE0063E88E /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 17CE32C9C8C4ABF27FB5A7CD /* Pods-AVPlayerHTTPHeaders.release.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
DEVELOPMENT_TEAM = V8SSGR3H48;
|
||||
INFOPLIST_FILE = AVPlayerHTTPHeaders/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.prophetstudios.AVPlayerHTTPHeaders;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
04F4C4151E301BCE0063E88E /* Build configuration list for PBXProject "AVPlayerHTTPHeaders" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
04F4C42F1E301BCE0063E88E /* Debug */,
|
||||
04F4C4301E301BCE0063E88E /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
04F4C4311E301BCE0063E88E /* Build configuration list for PBXNativeTarget "AVPlayerHTTPHeaders" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
04F4C4321E301BCE0063E88E /* Debug */,
|
||||
04F4C4331E301BCE0063E88E /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 04F4C4121E301BCE0063E88E /* Project object */;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:AVPlayerHTTPHeaders.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
@@ -0,0 +1,56 @@
|
||||
//
|
||||
// AVPlayerProxy.h
|
||||
// AVPlayerHTTPHeaders
|
||||
//
|
||||
// Created by Kevin Hunt on 2017-01-16.
|
||||
// Copyright © 2017 Prophet Studios. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
/**
|
||||
* The local host name and port for the player proxy
|
||||
*/
|
||||
OBJC_EXTERN NSString * __nonnull const AVPlayerProxyLocalHost;
|
||||
|
||||
OBJC_EXTERN NSString * __nonnull const AVPlayerReverseProxyDidReceiveHeadersNotification; /**< Notification sent when the proxy captures HTTP headers from a server response */
|
||||
|
||||
OBJC_EXTERN NSString * __nonnull const AVPlayerReverseProxyNotificationRequestURLKey; /**< NSString representing the URL of the request that was made */
|
||||
OBJC_EXTERN NSString * __nonnull const AVPlayerReverseProxyNotificationHeadersKey; /**< NSDictionary containing all key/value pairs from the HTTP headers of the response */
|
||||
|
||||
/**
|
||||
* A wrapper for the GCD webserver used to inject and insepect HTTP headers AVPlayer requests and responses
|
||||
* By running playback through a local proxy, we can add and modify headers in requests for manifests/chunks, as well as extract headers from the responses and pass them to the client for reporting and diagnostic purposes
|
||||
*
|
||||
* The daemon is started with a local port, 8080, as well a reverse proxy host to forward to.
|
||||
* If a request is sent to http://localhost:8080, the proxy will intercept the request, add any additional HTTP headers, then complete the request via the reverse proxy host on port 80, and finally return the response to the original request from the player.
|
||||
* Any HTTP headers received in the response are passed along to its listeners.
|
||||
*/
|
||||
@interface AVPlayerReverseProxy : NSObject
|
||||
|
||||
/**
|
||||
* Starts the AVPlayer proxy server listening to localhost.
|
||||
* Any requests sent to "http://localhost:8080" will be passed to "http://<reverseProxyHost>:80"
|
||||
*
|
||||
* @param reverseProxyHost The remote host for the reverse proxy
|
||||
*/
|
||||
- (void)startPlayerProxyWithReverseProxyHost:(nonnull NSString *)reverseProxyHost;
|
||||
|
||||
/**
|
||||
Stops the AVPlayer proxy
|
||||
*/
|
||||
- (void)stopPlayerProxy;
|
||||
|
||||
/**
|
||||
* Will add additional HTTP Headers to the manifest/chunk requests if supported.
|
||||
* @param httpHeaders The HTTP Headers to add
|
||||
*/
|
||||
- (void)addHttpHeaders:(nonnull NSDictionary *)httpHeaders;
|
||||
|
||||
/**
|
||||
* Will remove additional HTTP Headers to the manifest/chunk requests if supported.
|
||||
* @param httpHeaders The HTTP Headers to remove
|
||||
*/
|
||||
- (void)removeHttpHeaders:(nonnull NSDictionary *)httpHeaders;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,114 @@
|
||||
//
|
||||
// AVPlayerProxy.m
|
||||
// AVPlayerHTTPHeaders
|
||||
//
|
||||
// Created by Kevin Hunt on 2017-01-16.
|
||||
// Copyright © 2017 Prophet Studios. All rights reserved.
|
||||
//
|
||||
|
||||
#import "AVPlayerReverseProxy.h"
|
||||
#import "GCDWebServer.h"
|
||||
#import "GCDWebServerDataResponse.h"
|
||||
|
||||
NSString * __nonnull const AVPlayerProxyLocalHost = @"localhost:8080";
|
||||
|
||||
NSString *const AVPlayerReverseProxyDidReceiveHeadersNotification = @"AVPlayerReverseProxyDidReceiveHeadersNotification";
|
||||
|
||||
NSString *const AVPlayerReverseProxyNotificationRequestURLKey = @"AVPlayerReverseProxyNotificationRequestURLKey";
|
||||
NSString *const AVPlayerReverseProxyNotificationHeadersKey = @"AVPlayerReverseProxyNotificationHeadersKey";
|
||||
|
||||
@implementation AVPlayerReverseProxy {
|
||||
NSDictionary *_httpHeaders;
|
||||
GCDWebServer *_webServer;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
if (self = [super init]) {
|
||||
_httpHeaders = [[NSDictionary alloc] init];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)startPlayerProxyWithReverseProxyHost:(nonnull NSString *)reverseProxyHost {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
|
||||
_webServer = [[GCDWebServer alloc] init];
|
||||
|
||||
__weak NSDictionary *weakHeaders = _httpHeaders;
|
||||
__weak typeof(self) weakSelf = self;
|
||||
// Add a handler to respond to GET requests on any local URL
|
||||
[_webServer addDefaultHandlerForMethod:@"GET"
|
||||
requestClass:[GCDWebServerRequest class]
|
||||
processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
|
||||
|
||||
// Process the request by sending it using the reverse proxy URL
|
||||
GCDWebServerResponse *response = [weakSelf sendRequest:request toHost:reverseProxyHost withHeaders:weakHeaders];
|
||||
return response;
|
||||
}];
|
||||
|
||||
// Start server on port 8080
|
||||
[_webServer startWithPort:8080 bonjourName:nil];
|
||||
});
|
||||
}
|
||||
- (void)stopPlayerProxy {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[_webServer stop];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)addHttpHeaders:(NSDictionary*)httpHeaders {
|
||||
if (httpHeaders && [httpHeaders count] > 0) {
|
||||
NSMutableDictionary *mergeDict = [NSMutableDictionary dictionaryWithDictionary:_httpHeaders];
|
||||
[mergeDict addEntriesFromDictionary:httpHeaders];
|
||||
_httpHeaders = [NSDictionary dictionaryWithDictionary:mergeDict];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)removeHttpHeaders:(NSDictionary*)httpHeaders {
|
||||
if (httpHeaders && [httpHeaders count] > 0) {
|
||||
NSMutableDictionary *removeDict = [NSMutableDictionary dictionaryWithDictionary:_httpHeaders];
|
||||
[removeDict removeObjectsForKeys:[httpHeaders allKeys]];
|
||||
_httpHeaders = [NSDictionary dictionaryWithDictionary:removeDict];
|
||||
}
|
||||
}
|
||||
|
||||
- (GCDWebServerResponse *)sendRequest:(GCDWebServerRequest *)request toHost:(NSString *)reverseProxyHost withHeaders:(NSDictionary *)headers {
|
||||
NSError *error = nil;
|
||||
NSHTTPURLResponse *urlResponse = nil;
|
||||
|
||||
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
// Replace the local url with the reverse host to recreate the original url
|
||||
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
NSString *customUrl = [request.URL.absoluteString stringByReplacingOccurrencesOfString:AVPlayerProxyLocalHost withString:reverseProxyHost];
|
||||
|
||||
NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:customUrl]];
|
||||
|
||||
// Set the additional HTTP headers in the new request
|
||||
for (NSString *key in [headers allKeys]) {
|
||||
NSString *value = [headers valueForKey:key];
|
||||
[urlRequest setValue:value forHTTPHeaderField:key];
|
||||
}
|
||||
|
||||
// Synchronously make the request
|
||||
NSData *responseData = [NSURLConnection sendSynchronousRequest:urlRequest returningResponse:&urlResponse error:&error];
|
||||
|
||||
// Capture the header info
|
||||
NSDictionary *responseHeaders = urlResponse.allHeaderFields;
|
||||
NSString *contentType = [responseHeaders valueForKey:@"Content-Type"];
|
||||
|
||||
// Post notification containing headers an corresponding URL
|
||||
NSDictionary *userInfoDict = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
customUrl, AVPlayerReverseProxyNotificationRequestURLKey,
|
||||
responseHeaders, AVPlayerReverseProxyNotificationHeadersKey,
|
||||
nil];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:AVPlayerReverseProxyDidReceiveHeadersNotification
|
||||
object:nil
|
||||
userInfo:userInfoDict];
|
||||
|
||||
// Create the response to return back to the player
|
||||
GCDWebServerDataResponse *response = [GCDWebServerDataResponse responseWithData:responseData contentType:contentType];
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,17 @@
|
||||
//
|
||||
// AppDelegate.h
|
||||
// AVPlayerHTTPHeaders
|
||||
//
|
||||
// Created by Kevin Hunt on 2017-01-18.
|
||||
// Copyright © 2017 Prophet Studios. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface AppDelegate : UIResponder <UIApplicationDelegate>
|
||||
|
||||
@property (strong, nonatomic) UIWindow *window;
|
||||
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
//
|
||||
// AppDelegate.m
|
||||
// AVPlayerHTTPHeaders
|
||||
//
|
||||
// Created by Kevin Hunt on 2017-01-18.
|
||||
// Copyright © 2017 Prophet Studios. All rights reserved.
|
||||
//
|
||||
|
||||
#import "AppDelegate.h"
|
||||
|
||||
@interface AppDelegate ()
|
||||
|
||||
@end
|
||||
|
||||
@implementation AppDelegate
|
||||
|
||||
|
||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
|
||||
// Override point for customization after application launch.
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
||||
- (void)applicationWillResignActive:(UIApplication *)application {
|
||||
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
|
||||
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
|
||||
}
|
||||
|
||||
|
||||
- (void)applicationDidEnterBackground:(UIApplication *)application {
|
||||
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
|
||||
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
|
||||
}
|
||||
|
||||
|
||||
- (void)applicationWillEnterForeground:(UIApplication *)application {
|
||||
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
|
||||
}
|
||||
|
||||
|
||||
- (void)applicationDidBecomeActive:(UIApplication *)application {
|
||||
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
|
||||
}
|
||||
|
||||
|
||||
- (void)applicationWillTerminate:(UIApplication *)application {
|
||||
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,68 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"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",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "29x29",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "40x40",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "40x40",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "76x76",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "76x76",
|
||||
"scale" : "2x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="11134" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11106"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--View Controller-->
|
||||
<scene sceneID="EHf-IW-A2E">
|
||||
<objects>
|
||||
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
|
||||
<layoutGuides>
|
||||
<viewControllerLayoutGuide type="top" id="Llm-lL-Icb"/>
|
||||
<viewControllerLayoutGuide type="bottom" id="xb3-aO-Qok"/>
|
||||
</layoutGuides>
|
||||
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</view>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="53" y="375"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
||||
@@ -0,0 +1,89 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="11762" systemVersion="16C67" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
|
||||
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--Playback Headers View Controller-->
|
||||
<scene sceneID="tne-QT-ifu">
|
||||
<objects>
|
||||
<viewController id="BYZ-38-t0r" customClass="PlaybackHeadersViewController" sceneMemberID="viewController">
|
||||
<layoutGuides>
|
||||
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
|
||||
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
|
||||
</layoutGuides>
|
||||
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" text="http://devimages.apple.com/samplecode/AVARLDelegateDemo/BipBop_gear3_segmented/redirect_prog_index.m3u8" borderStyle="roundedRect" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="eJO-sl-25s">
|
||||
<rect key="frame" x="16" y="20" width="287" height="30"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="30" id="RZf-Hc-fmt"/>
|
||||
</constraints>
|
||||
<nil key="textColor"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits"/>
|
||||
<connections>
|
||||
<outlet property="delegate" destination="BYZ-38-t0r" id="idX-tq-Z7D"/>
|
||||
</connections>
|
||||
</textField>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="iYE-rb-frH">
|
||||
<rect key="frame" x="313" y="20" width="46" height="30"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="30" id="cXa-wV-4KT"/>
|
||||
<constraint firstAttribute="width" constant="46" id="ean-ea-PPY"/>
|
||||
</constraints>
|
||||
<state key="normal" title="Play"/>
|
||||
<connections>
|
||||
<action selector="playButtonPressed:" destination="BYZ-38-t0r" eventType="touchUpInside" id="yuh-Vf-GMU"/>
|
||||
</connections>
|
||||
</button>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="wnV-y0-3sx" userLabel="playerView">
|
||||
<rect key="frame" x="16" y="58" width="343" height="150"/>
|
||||
<color key="backgroundColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="150" id="Xef-76-mws"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="mcH-70-3t0" userLabel="textView">
|
||||
<rect key="frame" x="16" y="218" width="343" height="441"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="12"/>
|
||||
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
|
||||
</textView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="eJO-sl-25s" firstAttribute="top" secondItem="y3c-jy-aDJ" secondAttribute="bottom" id="6Gf-gb-t75"/>
|
||||
<constraint firstItem="wnV-y0-3sx" firstAttribute="top" secondItem="eJO-sl-25s" secondAttribute="bottom" constant="8" id="8sk-WT-fgZ"/>
|
||||
<constraint firstAttribute="trailingMargin" secondItem="wnV-y0-3sx" secondAttribute="trailing" id="A07-7J-7Dt"/>
|
||||
<constraint firstItem="wfy-db-euE" firstAttribute="top" secondItem="mcH-70-3t0" secondAttribute="bottom" constant="8" id="E2A-wZ-uRj"/>
|
||||
<constraint firstAttribute="leadingMargin" secondItem="mcH-70-3t0" secondAttribute="leading" id="F3i-fm-38J"/>
|
||||
<constraint firstItem="iYE-rb-frH" firstAttribute="top" secondItem="y3c-jy-aDJ" secondAttribute="bottom" id="GG2-yA-Bwz"/>
|
||||
<constraint firstItem="iYE-rb-frH" firstAttribute="leading" secondItem="eJO-sl-25s" secondAttribute="trailing" constant="10" id="HVl-Iu-Okw"/>
|
||||
<constraint firstAttribute="trailingMargin" secondItem="iYE-rb-frH" secondAttribute="trailing" id="XbI-wy-GYv"/>
|
||||
<constraint firstAttribute="leadingMargin" secondItem="wnV-y0-3sx" secondAttribute="leading" id="gbp-lF-Yon"/>
|
||||
<constraint firstAttribute="trailingMargin" secondItem="mcH-70-3t0" secondAttribute="trailing" id="kEg-No-BDt"/>
|
||||
<constraint firstItem="mcH-70-3t0" firstAttribute="top" secondItem="wnV-y0-3sx" secondAttribute="bottom" constant="10" id="lrW-5V-aa6"/>
|
||||
<constraint firstAttribute="leadingMargin" secondItem="eJO-sl-25s" secondAttribute="leading" id="qW8-on-zVl"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="playPauseButton" destination="iYE-rb-frH" id="GLT-qE-Qhd"/>
|
||||
<outlet property="playerView" destination="wnV-y0-3sx" id="nxL-gI-V6g"/>
|
||||
<outlet property="textView" destination="mcH-70-3t0" id="CcW-RB-r6g"/>
|
||||
<outlet property="urlTextField" destination="eJO-sl-25s" id="Q7Z-q5-PNX"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="136.80000000000001" y="137.18140929535232"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
||||
@@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>armv7</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<true/>
|
||||
</dict>
|
||||
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// PlaybackHeadersViewController.h
|
||||
// AVPlayerHTTPHeaders
|
||||
//
|
||||
// Created by Kevin Hunt on 2017-01-18.
|
||||
// Copyright © 2017 Prophet Studios. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface PlaybackHeadersViewController : UIViewController
|
||||
|
||||
@property (strong, nonatomic) IBOutlet UITextField *urlTextField;
|
||||
@property (strong, nonatomic) IBOutlet UIButton *playPauseButton;
|
||||
@property (strong, nonatomic) IBOutlet UIView *playerView;
|
||||
@property (strong, nonatomic) IBOutlet UITextView *textView;
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
//
|
||||
// PlaybackHeadersViewController.m
|
||||
// AVPlayerHTTPHeaders
|
||||
//
|
||||
// Created by Kevin Hunt on 2017-01-18.
|
||||
// Copyright © 2017 Prophet Studios. All rights reserved.
|
||||
//
|
||||
|
||||
#import "PlaybackHeadersViewController.h"
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#import "AVPlayerReverseProxy.h"
|
||||
|
||||
@interface PlaybackHeadersViewController () <UITextFieldDelegate>
|
||||
|
||||
@end
|
||||
|
||||
@implementation PlaybackHeadersViewController {
|
||||
AVPlayer *_avPlayer;
|
||||
AVPlayerReverseProxy *_playerReverseProxy;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
// Do any additional setup after loading the view, typically from a nib.
|
||||
_playerReverseProxy = [[AVPlayerReverseProxy alloc] init];
|
||||
}
|
||||
|
||||
|
||||
- (void)didReceiveMemoryWarning {
|
||||
[super didReceiveMemoryWarning];
|
||||
// Dispose of any resources that can be recreated.
|
||||
}
|
||||
|
||||
- (IBAction)playButtonPressed:(id)sender {
|
||||
|
||||
if (_avPlayer != NULL) {
|
||||
// Remove observers
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerReverseProxyDidReceiveHeadersNotification object:nil];
|
||||
|
||||
// Stop the player first
|
||||
[_avPlayer pause];
|
||||
_avPlayer = nil;
|
||||
|
||||
// Stop Player Proxy
|
||||
[_playerReverseProxy stopPlayerProxy];
|
||||
|
||||
_textView.text = @"";
|
||||
}
|
||||
|
||||
// Grab the URL for playback
|
||||
NSString *url = _urlTextField.text;
|
||||
|
||||
// Get external domain to be used for the reverse proxy
|
||||
NSURL *externalUrl = [NSURL URLWithString:url];
|
||||
NSString *externalDomain = [externalUrl host];
|
||||
|
||||
// Set up the reverse proxy by passing it the reverse host and setting the headers as needed
|
||||
NSDictionary *httpHeaders = [NSDictionary dictionaryWithObject:@"X-SOME-HEADER" forKey:@"Pragma"];
|
||||
[_playerReverseProxy addHttpHeaders:httpHeaders];
|
||||
[_playerReverseProxy startPlayerProxyWithReverseProxyHost:externalDomain];
|
||||
|
||||
// Crete observer to receive header info from responses
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handlePlayerProxyReceivedHeadersNotification:) name:AVPlayerReverseProxyDidReceiveHeadersNotification object:nil];
|
||||
|
||||
|
||||
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
// Switch the URL with the local host so that it passes through the proxy
|
||||
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
NSString *customUrl = [url stringByReplacingOccurrencesOfString:externalDomain withString:AVPlayerProxyLocalHost];
|
||||
AVURLAsset *urlAsset = [AVURLAsset URLAssetWithURL:[NSURL URLWithString:customUrl] options:nil];
|
||||
|
||||
// Create the player and add it to the playerView
|
||||
AVPlayerItem *playerItem = [AVPlayerItem playerItemWithAsset:urlAsset];
|
||||
_avPlayer = [AVPlayer playerWithPlayerItem:playerItem];
|
||||
AVPlayerLayer* playerLayer = [AVPlayerLayer playerLayerWithPlayer:_avPlayer];
|
||||
[playerLayer setBackgroundColor:[UIColor blackColor].CGColor];
|
||||
playerLayer.frame = _playerView.bounds;
|
||||
playerLayer.videoGravity = AVLayerVideoGravityResizeAspect;
|
||||
playerLayer.needsDisplayOnBoundsChange = YES;
|
||||
[_playerView.layer addSublayer:playerLayer];
|
||||
[_avPlayer play];
|
||||
|
||||
_textView.text = @"Starting Playback";
|
||||
}
|
||||
|
||||
- (void)handlePlayerProxyReceivedHeadersNotification:(NSNotification *)notification {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
// Get headers and corresponding URL from notification
|
||||
NSDictionary *httpHeaders = [[notification userInfo] objectForKey:AVPlayerReverseProxyNotificationHeadersKey];
|
||||
NSString *requestUrl = [[notification userInfo] objectForKey:AVPlayerReverseProxyNotificationRequestURLKey];
|
||||
|
||||
// Update UI
|
||||
NSString *headerText = [NSString stringWithFormat:@"URL: %@\nHeaders:%@\n\n\n", requestUrl, httpHeaders];
|
||||
_textView.text = [headerText stringByAppendingString:_textView.text];
|
||||
});
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark UITextFieldDelegate
|
||||
|
||||
- (BOOL)textFieldShouldReturn:(UITextField *)textField
|
||||
{
|
||||
[_urlTextField resignFirstResponder];
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,16 @@
|
||||
//
|
||||
// main.m
|
||||
// AVPlayerHTTPHeaders
|
||||
//
|
||||
// Created by Kevin Hunt on 2017-01-18.
|
||||
// Copyright © 2017 Prophet Studios. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "AppDelegate.h"
|
||||
|
||||
int main(int argc, char * argv[]) {
|
||||
@autoreleasepool {
|
||||
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
# Uncomment the next line to define a global platform for your project
|
||||
# platform :ios, '9.0'
|
||||
|
||||
target 'AVPlayerHTTPHeaders' do
|
||||
# Uncomment the next line if you're using Swift or would like to use dynamic frameworks
|
||||
# use_frameworks!
|
||||
|
||||
# Pods for AVPlayerHTTPHeaders
|
||||
pod "GCDWebServer", "~> 3.0"
|
||||
|
||||
end
|
||||
@@ -0,0 +1,47 @@
|
||||
AVPlayerHTTPHeaders Example
|
||||
===========================
|
||||
|
||||
A working example of how to augment and retrieve HTTP headers on requests made by the iOS AVPlayer using a reverse proxy server on the device.
|
||||
|
||||
Overview
|
||||
========
|
||||
|
||||
This sample application demonstrates how an application can inject additional HTTP headers into playlist and chunk requests made by the iOS AVPlayer while also retrieving the headers of the responses. This is particularly useful when debugging server issues, as adding additional headers can trigger the server to return additional diagnostic information in the requests.
|
||||
|
||||
Unfortunately, there are currently no APIs available in iOS to get and set this header information for requests made by the AVPlayer. For normal requests, we can simply use NSMutableURLRequest and NSHTTPURLResponse, but there is no OFFICIAL way to do this according to Apple.
|
||||
|
||||
There is a way to add additional headers using the undocumented options key, ```AVURLAssetHTTPHeaderFieldsKey``` when creating an AVURLAsset object, as shown below. However, It has been strongly warned online that Apple may reject applications that use this key, and I could find no official word on the Apple Developer forums on whether or not this would be accepted:
|
||||
```
|
||||
AVURLAsset *urlAsset = [AVURLAsset URLAssetWithURL:[NSURL URLWithString:customUrl] options:@{@"AVURLAssetHTTPHeaderFieldsKey" : httpHeaders}];
|
||||
```
|
||||
|
||||
The only alternative is to run a reverse proxy server on the device, allowing AVPlayer to pass requests through it, which are then intercepted, modified, sent to their original destination, and then examined when a response is returned.
|
||||
|
||||
When a new playback session is started, the client instantiates and starts a local HTTP server on the device, running at http://localhost:8080. Any requests sent to this will allow the proxy to intercept the request, add any additional HTTP headers, then complete the request via the reverse proxy host on port 80, and finally return the response to the original request from the player. Any HTTP headers received in the response are passed along to the client for reporting/diagnostic purposes.
|
||||
|
||||
The flow is as follows:
|
||||
* Client receives request to play content at http://someurl.com/some_manifest.m3u8
|
||||
* Client starts the local HTTP server at http://localhost:8080 and passes it "someurl.com" as the reverse proxy host name
|
||||
* Client creates the AVPlayer, passing it http://localhost:8080/some_manifest.m3u8
|
||||
* AVPlayer tries to start playback by making a network request to http://localhost:8080/some_manifest.m3u8, which is intercepted by the proxy
|
||||
* Proxy reconstructs what should have been the original request using the reverse proxy host it was passed
|
||||
* Proxy makes the external request to the server at http://someurl.com/some_manifest.m3u8 with the additional headers
|
||||
* Proxy extracts the headers from the response and sends them to its listeners
|
||||
* Proxy returns the data and headers from the request to http://someurl.com/some_manifest.m3u8 as the response to http://localhost:8080/some_manifest.m3u8
|
||||
* AVPlayer uses this data to start playback
|
||||
|
||||
This process is repeated for any variant playlists and chunk URLs that are returned in the manifest. AVPlayer makes calls to http://localhost:8080/chunk_01.ts, and the proxy gets the actual data for it from http://someurl.com/chunk_01.ts, modifying and extracting the headers as it does so.
|
||||
|
||||
Getting Started
|
||||
===============
|
||||
1. Download or checkout the latest release to your machine.
|
||||
2. In the root directory, run the following to download and install GCDWebServer:
|
||||
```
|
||||
$ pod install
|
||||
```
|
||||
3. Open AVPlayerHTTPHeaders.xcworkspace and run the target.
|
||||
4. Tap the Play button to start playback of the URL. The header information for each request made by the AVPlayer will be logged below the video.
|
||||
|
||||
The GCDWebServer
|
||||
================
|
||||
I used the [GCDWebServer](https://github.com/swisspol/GCDWebServer) as the base for my reverse proxy because it was incredibly easy to set up and did precisely what I needed it to do. It is also apparently possible to use Mongoose, as has been done [here](https://github.com/masterjk/ios-avplayer-http-capture)
|
||||
Reference in New Issue
Block a user