├── .gitignore ├── Cartfile ├── Cartfile.resolved ├── LICENSE ├── README.md ├── StreamIt.xcodeproj ├── project.pbxproj └── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ └── IDEWorkspaceChecks.plist └── StreamIt ├── AppDelegate.swift ├── Assets.xcassets ├── AppIcon.appiconset │ ├── Contents.json │ ├── Icon-App-20x20@1x.png │ ├── Icon-App-20x20@2x.png │ ├── Icon-App-20x20@3x.png │ ├── Icon-App-29x29@1x.png │ ├── Icon-App-29x29@2x.png │ ├── Icon-App-29x29@3x.png │ ├── Icon-App-40x40@1x.png │ ├── Icon-App-40x40@2x.png │ ├── Icon-App-40x40@3x.png │ ├── Icon-App-57x57@1x.png │ ├── Icon-App-57x57@2x.png │ ├── Icon-App-60x60@2x.png │ ├── Icon-App-60x60@3x.png │ ├── Icon-App-72x72@1x.png │ ├── Icon-App-72x72@2x.png │ ├── Icon-App-76x76@1x.png │ ├── Icon-App-76x76@2x.png │ ├── Icon-App-83.5x83.5@2x.png │ ├── Icon-Small-50x50@1x.png │ ├── Icon-Small-50x50@2x.png │ └── ItunesArtwork@2x.png ├── Contents.json ├── information.imageset │ ├── Contents.json │ └── Dialog-Box-About-128.png ├── led_gray.imageset │ ├── Contents.json │ └── led_gray.png └── led_red.imageset │ ├── Contents.json │ └── led_red.png ├── Base.lproj ├── LaunchScreen.storyboard └── Main.storyboard ├── CameraViewController.swift ├── IPChecker ├── IPChecker.h └── IPChecker.m ├── Info.plist ├── Queue.swift ├── StreamIt-Bridging-Header.h └── StreamingSession.swift /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | RxFlow.framework.zip 9 | undocumented.json 10 | 11 | ## macos 12 | .DS_Store 13 | 14 | ## Various settings 15 | *.pbxuser 16 | !default.pbxuser 17 | *.mode1v3 18 | !default.mode1v3 19 | *.mode2v3 20 | !default.mode2v3 21 | *.perspectivev3 22 | !default.perspectivev3 23 | xcuserdata/ 24 | 25 | ## Other 26 | *.moved-aside 27 | *.xccheckout 28 | *.xcscmblueprint 29 | 30 | ## Obj-C/Swift specific 31 | *.hmap 32 | *.ipa 33 | *.dSYM.zip 34 | *.dSYM 35 | 36 | ## Playgrounds 37 | timeline.xctimeline 38 | playground.xcworkspace 39 | 40 | # Swift Package Manager 41 | # 42 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 43 | # Packages/ 44 | # Package.pins 45 | .build/ 46 | 47 | # CocoaPods 48 | # 49 | # We recommend against adding the Pods directory to your .gitignore. However 50 | # you should judge for yourself, the pros and cons are mentioned at: 51 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 52 | # 53 | # Pods/ 54 | 55 | # Carthage 56 | # 57 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 58 | Carthage 59 | Carthage/Checkouts 60 | Carthage/Build 61 | 62 | # fastlane 63 | # 64 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 65 | # screenshots whenever they are needed. 66 | # For more information about the recommended setup visit: 67 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 68 | 69 | fastlane/report.xml 70 | fastlane/Preview.html 71 | fastlane/screenshots 72 | fastlane/test_output 73 | 74 | .idea 75 | Package.resolved 76 | .swiftpm 77 | -------------------------------------------------------------------------------- /Cartfile: -------------------------------------------------------------------------------- 1 | github "robbiehanson/CocoaAsyncSocket" "master" -------------------------------------------------------------------------------- /Cartfile.resolved: -------------------------------------------------------------------------------- 1 | github "robbiehanson/CocoaAsyncSocket" "53a1298f4bbb735a1e7ab235f6e74cf099a1583b" 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Thibault Wittemberg 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # StreamIt 2 | This iOS app turns your iOS device into a streaming camera. 3 | 4 | Simply launch the app and tap the screen to display your streaming URL. Copy this URL in a web browser and that's it. 5 | 6 | Beware, this works only on a local network (not over the Internet). 7 | -------------------------------------------------------------------------------- /StreamIt.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1A25D45D1EA27636002431F2 /* CocoaAsyncSocket.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1A7E8C4A1CBFFE3F00EFE574 /* CocoaAsyncSocket.framework */; }; 11 | 1A7E8C391CBFFC7400EFE574 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A7E8C381CBFFC7400EFE574 /* AppDelegate.swift */; }; 12 | 1A7E8C3E1CBFFC7400EFE574 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 1A7E8C3C1CBFFC7400EFE574 /* Main.storyboard */; }; 13 | 1A7E8C401CBFFC7400EFE574 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1A7E8C3F1CBFFC7400EFE574 /* Assets.xcassets */; }; 14 | 1A7E8C431CBFFC7400EFE574 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 1A7E8C411CBFFC7400EFE574 /* LaunchScreen.storyboard */; }; 15 | 1A7E8C541CBFFF9A00EFE574 /* CameraViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A7E8C531CBFFF9A00EFE574 /* CameraViewController.swift */; }; 16 | 1A7E8C561CC0005800EFE574 /* StreamingSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A7E8C551CC0005800EFE574 /* StreamingSession.swift */; }; 17 | 1A7E8C5B1CC0009500EFE574 /* Queue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A7E8C571CC0009500EFE574 /* Queue.swift */; }; 18 | 93DE98311FDEB62A0048600F /* IPChecker.m in Sources */ = {isa = PBXBuildFile; fileRef = 93DE98301FDEB62A0048600F /* IPChecker.m */; }; 19 | /* End PBXBuildFile section */ 20 | 21 | /* Begin PBXFileReference section */ 22 | 1A7E8C351CBFFC7400EFE574 /* StreamIt.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = StreamIt.app; sourceTree = BUILT_PRODUCTS_DIR; }; 23 | 1A7E8C381CBFFC7400EFE574 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 24 | 1A7E8C3D1CBFFC7400EFE574 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 25 | 1A7E8C3F1CBFFC7400EFE574 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 26 | 1A7E8C421CBFFC7400EFE574 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 27 | 1A7E8C441CBFFC7400EFE574 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 28 | 1A7E8C4A1CBFFE3F00EFE574 /* CocoaAsyncSocket.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CocoaAsyncSocket.framework; path = Carthage/Build/iOS/CocoaAsyncSocket.framework; sourceTree = ""; }; 29 | 1A7E8C4E1CBFFEE900EFE574 /* StreamIt-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "StreamIt-Bridging-Header.h"; sourceTree = ""; }; 30 | 1A7E8C531CBFFF9A00EFE574 /* CameraViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CameraViewController.swift; sourceTree = ""; }; 31 | 1A7E8C551CC0005800EFE574 /* StreamingSession.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StreamingSession.swift; sourceTree = ""; }; 32 | 1A7E8C571CC0009500EFE574 /* Queue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Queue.swift; sourceTree = ""; }; 33 | 93DE982F1FDEB62A0048600F /* IPChecker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IPChecker.h; sourceTree = ""; }; 34 | 93DE98301FDEB62A0048600F /* IPChecker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IPChecker.m; sourceTree = ""; }; 35 | /* End PBXFileReference section */ 36 | 37 | /* Begin PBXFrameworksBuildPhase section */ 38 | 1A7E8C321CBFFC7400EFE574 /* Frameworks */ = { 39 | isa = PBXFrameworksBuildPhase; 40 | buildActionMask = 2147483647; 41 | files = ( 42 | 1A25D45D1EA27636002431F2 /* CocoaAsyncSocket.framework in Frameworks */, 43 | ); 44 | runOnlyForDeploymentPostprocessing = 0; 45 | }; 46 | /* End PBXFrameworksBuildPhase section */ 47 | 48 | /* Begin PBXGroup section */ 49 | 1A7E8C2C1CBFFC7400EFE574 = { 50 | isa = PBXGroup; 51 | children = ( 52 | 1A7E8C4A1CBFFE3F00EFE574 /* CocoaAsyncSocket.framework */, 53 | 1A7E8C371CBFFC7400EFE574 /* StreamIt */, 54 | 1A7E8C361CBFFC7400EFE574 /* Products */, 55 | ); 56 | sourceTree = ""; 57 | }; 58 | 1A7E8C361CBFFC7400EFE574 /* Products */ = { 59 | isa = PBXGroup; 60 | children = ( 61 | 1A7E8C351CBFFC7400EFE574 /* StreamIt.app */, 62 | ); 63 | name = Products; 64 | sourceTree = ""; 65 | }; 66 | 1A7E8C371CBFFC7400EFE574 /* StreamIt */ = { 67 | isa = PBXGroup; 68 | children = ( 69 | 1A7E8C381CBFFC7400EFE574 /* AppDelegate.swift */, 70 | 1A7E8C531CBFFF9A00EFE574 /* CameraViewController.swift */, 71 | 1A7E8C551CC0005800EFE574 /* StreamingSession.swift */, 72 | 1A7E8C571CC0009500EFE574 /* Queue.swift */, 73 | 1A7E8C3C1CBFFC7400EFE574 /* Main.storyboard */, 74 | 1A7E8C3F1CBFFC7400EFE574 /* Assets.xcassets */, 75 | 1A7E8C411CBFFC7400EFE574 /* LaunchScreen.storyboard */, 76 | 1A7E8C441CBFFC7400EFE574 /* Info.plist */, 77 | 93DE982E1FDEB62A0048600F /* IPChecker */, 78 | 1A7E8C4E1CBFFEE900EFE574 /* StreamIt-Bridging-Header.h */, 79 | ); 80 | path = StreamIt; 81 | sourceTree = ""; 82 | }; 83 | 93DE982E1FDEB62A0048600F /* IPChecker */ = { 84 | isa = PBXGroup; 85 | children = ( 86 | 93DE982F1FDEB62A0048600F /* IPChecker.h */, 87 | 93DE98301FDEB62A0048600F /* IPChecker.m */, 88 | ); 89 | path = IPChecker; 90 | sourceTree = ""; 91 | }; 92 | /* End PBXGroup section */ 93 | 94 | /* Begin PBXNativeTarget section */ 95 | 1A7E8C341CBFFC7400EFE574 /* StreamIt */ = { 96 | isa = PBXNativeTarget; 97 | buildConfigurationList = 1A7E8C471CBFFC7400EFE574 /* Build configuration list for PBXNativeTarget "StreamIt" */; 98 | buildPhases = ( 99 | 1A7E8C311CBFFC7400EFE574 /* Sources */, 100 | 1A7E8C321CBFFC7400EFE574 /* Frameworks */, 101 | 1A7E8C331CBFFC7400EFE574 /* Resources */, 102 | 1A7E8C4C1CBFFE5400EFE574 /* ShellScript */, 103 | ); 104 | buildRules = ( 105 | ); 106 | dependencies = ( 107 | ); 108 | name = StreamIt; 109 | productName = StreamIt; 110 | productReference = 1A7E8C351CBFFC7400EFE574 /* StreamIt.app */; 111 | productType = "com.apple.product-type.application"; 112 | }; 113 | /* End PBXNativeTarget section */ 114 | 115 | /* Begin PBXProject section */ 116 | 1A7E8C2D1CBFFC7400EFE574 /* Project object */ = { 117 | isa = PBXProject; 118 | attributes = { 119 | LastSwiftUpdateCheck = 0730; 120 | LastUpgradeCheck = 1130; 121 | ORGANIZATIONNAME = "Thibault Wittemberg"; 122 | TargetAttributes = { 123 | 1A7E8C341CBFFC7400EFE574 = { 124 | CreatedOnToolsVersion = 7.3; 125 | DevelopmentTeam = 3V5265LQM9; 126 | LastSwiftMigration = 0920; 127 | }; 128 | }; 129 | }; 130 | buildConfigurationList = 1A7E8C301CBFFC7400EFE574 /* Build configuration list for PBXProject "StreamIt" */; 131 | compatibilityVersion = "Xcode 3.2"; 132 | developmentRegion = en; 133 | hasScannedForEncodings = 0; 134 | knownRegions = ( 135 | en, 136 | Base, 137 | ); 138 | mainGroup = 1A7E8C2C1CBFFC7400EFE574; 139 | productRefGroup = 1A7E8C361CBFFC7400EFE574 /* Products */; 140 | projectDirPath = ""; 141 | projectRoot = ""; 142 | targets = ( 143 | 1A7E8C341CBFFC7400EFE574 /* StreamIt */, 144 | ); 145 | }; 146 | /* End PBXProject section */ 147 | 148 | /* Begin PBXResourcesBuildPhase section */ 149 | 1A7E8C331CBFFC7400EFE574 /* Resources */ = { 150 | isa = PBXResourcesBuildPhase; 151 | buildActionMask = 2147483647; 152 | files = ( 153 | 1A7E8C431CBFFC7400EFE574 /* LaunchScreen.storyboard in Resources */, 154 | 1A7E8C401CBFFC7400EFE574 /* Assets.xcassets in Resources */, 155 | 1A7E8C3E1CBFFC7400EFE574 /* Main.storyboard in Resources */, 156 | ); 157 | runOnlyForDeploymentPostprocessing = 0; 158 | }; 159 | /* End PBXResourcesBuildPhase section */ 160 | 161 | /* Begin PBXShellScriptBuildPhase section */ 162 | 1A7E8C4C1CBFFE5400EFE574 /* ShellScript */ = { 163 | isa = PBXShellScriptBuildPhase; 164 | buildActionMask = 2147483647; 165 | files = ( 166 | ); 167 | inputPaths = ( 168 | "$(SRCROOT)/Carthage/Build/iOS/CocoaAsyncSocket.framework", 169 | ); 170 | outputPaths = ( 171 | ); 172 | runOnlyForDeploymentPostprocessing = 0; 173 | shellPath = /bin/sh; 174 | shellScript = "/usr/local/bin/carthage copy-frameworks"; 175 | }; 176 | /* End PBXShellScriptBuildPhase section */ 177 | 178 | /* Begin PBXSourcesBuildPhase section */ 179 | 1A7E8C311CBFFC7400EFE574 /* Sources */ = { 180 | isa = PBXSourcesBuildPhase; 181 | buildActionMask = 2147483647; 182 | files = ( 183 | 1A7E8C5B1CC0009500EFE574 /* Queue.swift in Sources */, 184 | 93DE98311FDEB62A0048600F /* IPChecker.m in Sources */, 185 | 1A7E8C391CBFFC7400EFE574 /* AppDelegate.swift in Sources */, 186 | 1A7E8C541CBFFF9A00EFE574 /* CameraViewController.swift in Sources */, 187 | 1A7E8C561CC0005800EFE574 /* StreamingSession.swift in Sources */, 188 | ); 189 | runOnlyForDeploymentPostprocessing = 0; 190 | }; 191 | /* End PBXSourcesBuildPhase section */ 192 | 193 | /* Begin PBXVariantGroup section */ 194 | 1A7E8C3C1CBFFC7400EFE574 /* Main.storyboard */ = { 195 | isa = PBXVariantGroup; 196 | children = ( 197 | 1A7E8C3D1CBFFC7400EFE574 /* Base */, 198 | ); 199 | name = Main.storyboard; 200 | sourceTree = ""; 201 | }; 202 | 1A7E8C411CBFFC7400EFE574 /* LaunchScreen.storyboard */ = { 203 | isa = PBXVariantGroup; 204 | children = ( 205 | 1A7E8C421CBFFC7400EFE574 /* Base */, 206 | ); 207 | name = LaunchScreen.storyboard; 208 | sourceTree = ""; 209 | }; 210 | /* End PBXVariantGroup section */ 211 | 212 | /* Begin XCBuildConfiguration section */ 213 | 1A7E8C451CBFFC7400EFE574 /* Debug */ = { 214 | isa = XCBuildConfiguration; 215 | buildSettings = { 216 | ALWAYS_SEARCH_USER_PATHS = NO; 217 | CLANG_ANALYZER_NONNULL = YES; 218 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 219 | CLANG_CXX_LIBRARY = "libc++"; 220 | CLANG_ENABLE_MODULES = YES; 221 | CLANG_ENABLE_OBJC_ARC = YES; 222 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 223 | CLANG_WARN_BOOL_CONVERSION = YES; 224 | CLANG_WARN_COMMA = YES; 225 | CLANG_WARN_CONSTANT_CONVERSION = YES; 226 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 227 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 228 | CLANG_WARN_EMPTY_BODY = YES; 229 | CLANG_WARN_ENUM_CONVERSION = YES; 230 | CLANG_WARN_INFINITE_RECURSION = YES; 231 | CLANG_WARN_INT_CONVERSION = YES; 232 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 233 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 234 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 235 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 236 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 237 | CLANG_WARN_STRICT_PROTOTYPES = YES; 238 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 239 | CLANG_WARN_UNREACHABLE_CODE = YES; 240 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 241 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 242 | COPY_PHASE_STRIP = NO; 243 | DEBUG_INFORMATION_FORMAT = dwarf; 244 | ENABLE_STRICT_OBJC_MSGSEND = YES; 245 | ENABLE_TESTABILITY = YES; 246 | GCC_C_LANGUAGE_STANDARD = gnu99; 247 | GCC_DYNAMIC_NO_PIC = NO; 248 | GCC_NO_COMMON_BLOCKS = YES; 249 | GCC_OPTIMIZATION_LEVEL = 0; 250 | GCC_PREPROCESSOR_DEFINITIONS = ( 251 | "DEBUG=1", 252 | "$(inherited)", 253 | ); 254 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 255 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 256 | GCC_WARN_UNDECLARED_SELECTOR = YES; 257 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 258 | GCC_WARN_UNUSED_FUNCTION = YES; 259 | GCC_WARN_UNUSED_VARIABLE = YES; 260 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 261 | MTL_ENABLE_DEBUG_INFO = YES; 262 | ONLY_ACTIVE_ARCH = YES; 263 | SDKROOT = iphoneos; 264 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 265 | TARGETED_DEVICE_FAMILY = "1,2"; 266 | }; 267 | name = Debug; 268 | }; 269 | 1A7E8C461CBFFC7400EFE574 /* Release */ = { 270 | isa = XCBuildConfiguration; 271 | buildSettings = { 272 | ALWAYS_SEARCH_USER_PATHS = NO; 273 | CLANG_ANALYZER_NONNULL = YES; 274 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 275 | CLANG_CXX_LIBRARY = "libc++"; 276 | CLANG_ENABLE_MODULES = YES; 277 | CLANG_ENABLE_OBJC_ARC = YES; 278 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 279 | CLANG_WARN_BOOL_CONVERSION = YES; 280 | CLANG_WARN_COMMA = YES; 281 | CLANG_WARN_CONSTANT_CONVERSION = YES; 282 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 283 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 284 | CLANG_WARN_EMPTY_BODY = YES; 285 | CLANG_WARN_ENUM_CONVERSION = YES; 286 | CLANG_WARN_INFINITE_RECURSION = YES; 287 | CLANG_WARN_INT_CONVERSION = YES; 288 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 289 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 290 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 291 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 292 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 293 | CLANG_WARN_STRICT_PROTOTYPES = YES; 294 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 295 | CLANG_WARN_UNREACHABLE_CODE = YES; 296 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 297 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 298 | COPY_PHASE_STRIP = NO; 299 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 300 | ENABLE_NS_ASSERTIONS = NO; 301 | ENABLE_STRICT_OBJC_MSGSEND = YES; 302 | GCC_C_LANGUAGE_STANDARD = gnu99; 303 | GCC_NO_COMMON_BLOCKS = YES; 304 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 305 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 306 | GCC_WARN_UNDECLARED_SELECTOR = YES; 307 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 308 | GCC_WARN_UNUSED_FUNCTION = YES; 309 | GCC_WARN_UNUSED_VARIABLE = YES; 310 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 311 | MTL_ENABLE_DEBUG_INFO = NO; 312 | SDKROOT = iphoneos; 313 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 314 | TARGETED_DEVICE_FAMILY = "1,2"; 315 | VALIDATE_PRODUCT = YES; 316 | }; 317 | name = Release; 318 | }; 319 | 1A7E8C481CBFFC7400EFE574 /* Debug */ = { 320 | isa = XCBuildConfiguration; 321 | buildSettings = { 322 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 323 | CLANG_ENABLE_MODULES = YES; 324 | DEVELOPMENT_TEAM = 3V5265LQM9; 325 | FRAMEWORK_SEARCH_PATHS = ( 326 | "$(inherited)", 327 | "$(PROJECT_DIR)/Carthage/Build/iOS", 328 | ); 329 | INFOPLIST_FILE = StreamIt/Info.plist; 330 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 331 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 332 | PRODUCT_BUNDLE_IDENTIFIER = com.warpcore.StreamIt; 333 | PRODUCT_NAME = "$(TARGET_NAME)"; 334 | SWIFT_OBJC_BRIDGING_HEADER = "StreamIt/StreamIt-Bridging-Header.h"; 335 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 336 | SWIFT_VERSION = 5.0; 337 | TARGETED_DEVICE_FAMILY = "1,2"; 338 | }; 339 | name = Debug; 340 | }; 341 | 1A7E8C491CBFFC7400EFE574 /* Release */ = { 342 | isa = XCBuildConfiguration; 343 | buildSettings = { 344 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 345 | CLANG_ENABLE_MODULES = YES; 346 | DEVELOPMENT_TEAM = 3V5265LQM9; 347 | FRAMEWORK_SEARCH_PATHS = ( 348 | "$(inherited)", 349 | "$(PROJECT_DIR)/Carthage/Build/iOS", 350 | ); 351 | INFOPLIST_FILE = StreamIt/Info.plist; 352 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 353 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 354 | PRODUCT_BUNDLE_IDENTIFIER = com.warpcore.StreamIt; 355 | PRODUCT_NAME = "$(TARGET_NAME)"; 356 | SWIFT_OBJC_BRIDGING_HEADER = "StreamIt/StreamIt-Bridging-Header.h"; 357 | SWIFT_VERSION = 5.0; 358 | TARGETED_DEVICE_FAMILY = "1,2"; 359 | }; 360 | name = Release; 361 | }; 362 | /* End XCBuildConfiguration section */ 363 | 364 | /* Begin XCConfigurationList section */ 365 | 1A7E8C301CBFFC7400EFE574 /* Build configuration list for PBXProject "StreamIt" */ = { 366 | isa = XCConfigurationList; 367 | buildConfigurations = ( 368 | 1A7E8C451CBFFC7400EFE574 /* Debug */, 369 | 1A7E8C461CBFFC7400EFE574 /* Release */, 370 | ); 371 | defaultConfigurationIsVisible = 0; 372 | defaultConfigurationName = Release; 373 | }; 374 | 1A7E8C471CBFFC7400EFE574 /* Build configuration list for PBXNativeTarget "StreamIt" */ = { 375 | isa = XCConfigurationList; 376 | buildConfigurations = ( 377 | 1A7E8C481CBFFC7400EFE574 /* Debug */, 378 | 1A7E8C491CBFFC7400EFE574 /* Release */, 379 | ); 380 | defaultConfigurationIsVisible = 0; 381 | defaultConfigurationName = Release; 382 | }; 383 | /* End XCConfigurationList section */ 384 | }; 385 | rootObject = 1A7E8C2D1CBFFC7400EFE574 /* Project object */; 386 | } 387 | -------------------------------------------------------------------------------- /StreamIt.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /StreamIt.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /StreamIt/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // StreamIt 4 | // 5 | // Created by Thibault Wittemberg on 14/04/2016. 6 | // Copyright © 2016 Thibault Wittemberg. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CocoaAsyncSocket 11 | 12 | @UIApplicationMain 13 | class AppDelegate: UIResponder, UIApplicationDelegate { 14 | 15 | var window: UIWindow? 16 | 17 | // MARK: - 18 | 19 | internal func application(_ application: UIApplication, 20 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 21 | return true 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /StreamIt/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "57x57", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-57x57@1x.png", 49 | "scale" : "1x" 50 | }, 51 | { 52 | "size" : "57x57", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-57x57@2x.png", 55 | "scale" : "2x" 56 | }, 57 | { 58 | "size" : "60x60", 59 | "idiom" : "iphone", 60 | "filename" : "Icon-App-60x60@2x.png", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "size" : "60x60", 65 | "idiom" : "iphone", 66 | "filename" : "Icon-App-60x60@3x.png", 67 | "scale" : "3x" 68 | }, 69 | { 70 | "size" : "20x20", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-20x20@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "20x20", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-20x20@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "29x29", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-29x29@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "29x29", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-29x29@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "40x40", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-40x40@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "40x40", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-40x40@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "50x50", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-Small-50x50@1x.png", 109 | "scale" : "1x" 110 | }, 111 | { 112 | "size" : "50x50", 113 | "idiom" : "ipad", 114 | "filename" : "Icon-Small-50x50@2x.png", 115 | "scale" : "2x" 116 | }, 117 | { 118 | "size" : "72x72", 119 | "idiom" : "ipad", 120 | "filename" : "Icon-App-72x72@1x.png", 121 | "scale" : "1x" 122 | }, 123 | { 124 | "size" : "72x72", 125 | "idiom" : "ipad", 126 | "filename" : "Icon-App-72x72@2x.png", 127 | "scale" : "2x" 128 | }, 129 | { 130 | "size" : "76x76", 131 | "idiom" : "ipad", 132 | "filename" : "Icon-App-76x76@1x.png", 133 | "scale" : "1x" 134 | }, 135 | { 136 | "size" : "76x76", 137 | "idiom" : "ipad", 138 | "filename" : "Icon-App-76x76@2x.png", 139 | "scale" : "2x" 140 | }, 141 | { 142 | "size" : "83.5x83.5", 143 | "idiom" : "ipad", 144 | "filename" : "Icon-App-83.5x83.5@2x.png", 145 | "scale" : "2x" 146 | }, 147 | { 148 | "size" : "1024x1024", 149 | "idiom" : "ios-marketing", 150 | "filename" : "ItunesArtwork@2x.png", 151 | "scale" : "1x" 152 | } 153 | ], 154 | "info" : { 155 | "version" : 1, 156 | "author" : "xcode" 157 | } 158 | } -------------------------------------------------------------------------------- /StreamIt/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twittemb/StreamIt/ef3ada1a8dc9c7a26bcccd061339f03d5a63c9fa/StreamIt/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /StreamIt/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twittemb/StreamIt/ef3ada1a8dc9c7a26bcccd061339f03d5a63c9fa/StreamIt/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /StreamIt/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twittemb/StreamIt/ef3ada1a8dc9c7a26bcccd061339f03d5a63c9fa/StreamIt/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /StreamIt/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twittemb/StreamIt/ef3ada1a8dc9c7a26bcccd061339f03d5a63c9fa/StreamIt/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /StreamIt/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twittemb/StreamIt/ef3ada1a8dc9c7a26bcccd061339f03d5a63c9fa/StreamIt/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /StreamIt/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twittemb/StreamIt/ef3ada1a8dc9c7a26bcccd061339f03d5a63c9fa/StreamIt/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /StreamIt/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twittemb/StreamIt/ef3ada1a8dc9c7a26bcccd061339f03d5a63c9fa/StreamIt/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /StreamIt/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twittemb/StreamIt/ef3ada1a8dc9c7a26bcccd061339f03d5a63c9fa/StreamIt/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /StreamIt/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twittemb/StreamIt/ef3ada1a8dc9c7a26bcccd061339f03d5a63c9fa/StreamIt/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /StreamIt/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twittemb/StreamIt/ef3ada1a8dc9c7a26bcccd061339f03d5a63c9fa/StreamIt/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png -------------------------------------------------------------------------------- /StreamIt/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twittemb/StreamIt/ef3ada1a8dc9c7a26bcccd061339f03d5a63c9fa/StreamIt/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png -------------------------------------------------------------------------------- /StreamIt/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twittemb/StreamIt/ef3ada1a8dc9c7a26bcccd061339f03d5a63c9fa/StreamIt/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /StreamIt/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twittemb/StreamIt/ef3ada1a8dc9c7a26bcccd061339f03d5a63c9fa/StreamIt/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /StreamIt/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twittemb/StreamIt/ef3ada1a8dc9c7a26bcccd061339f03d5a63c9fa/StreamIt/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png -------------------------------------------------------------------------------- /StreamIt/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twittemb/StreamIt/ef3ada1a8dc9c7a26bcccd061339f03d5a63c9fa/StreamIt/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png -------------------------------------------------------------------------------- /StreamIt/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twittemb/StreamIt/ef3ada1a8dc9c7a26bcccd061339f03d5a63c9fa/StreamIt/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /StreamIt/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twittemb/StreamIt/ef3ada1a8dc9c7a26bcccd061339f03d5a63c9fa/StreamIt/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /StreamIt/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twittemb/StreamIt/ef3ada1a8dc9c7a26bcccd061339f03d5a63c9fa/StreamIt/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /StreamIt/Assets.xcassets/AppIcon.appiconset/Icon-Small-50x50@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twittemb/StreamIt/ef3ada1a8dc9c7a26bcccd061339f03d5a63c9fa/StreamIt/Assets.xcassets/AppIcon.appiconset/Icon-Small-50x50@1x.png -------------------------------------------------------------------------------- /StreamIt/Assets.xcassets/AppIcon.appiconset/Icon-Small-50x50@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twittemb/StreamIt/ef3ada1a8dc9c7a26bcccd061339f03d5a63c9fa/StreamIt/Assets.xcassets/AppIcon.appiconset/Icon-Small-50x50@2x.png -------------------------------------------------------------------------------- /StreamIt/Assets.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twittemb/StreamIt/ef3ada1a8dc9c7a26bcccd061339f03d5a63c9fa/StreamIt/Assets.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png -------------------------------------------------------------------------------- /StreamIt/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /StreamIt/Assets.xcassets/information.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Dialog-Box-About-128.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /StreamIt/Assets.xcassets/information.imageset/Dialog-Box-About-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twittemb/StreamIt/ef3ada1a8dc9c7a26bcccd061339f03d5a63c9fa/StreamIt/Assets.xcassets/information.imageset/Dialog-Box-About-128.png -------------------------------------------------------------------------------- /StreamIt/Assets.xcassets/led_gray.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "led_gray.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /StreamIt/Assets.xcassets/led_gray.imageset/led_gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twittemb/StreamIt/ef3ada1a8dc9c7a26bcccd061339f03d5a63c9fa/StreamIt/Assets.xcassets/led_gray.imageset/led_gray.png -------------------------------------------------------------------------------- /StreamIt/Assets.xcassets/led_red.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "led_red.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /StreamIt/Assets.xcassets/led_red.imageset/led_red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twittemb/StreamIt/ef3ada1a8dc9c7a26bcccd061339f03d5a63c9fa/StreamIt/Assets.xcassets/led_red.imageset/led_red.png -------------------------------------------------------------------------------- /StreamIt/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /StreamIt/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 54 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | -------------------------------------------------------------------------------- /StreamIt/CameraViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CameraViewController.swift 3 | // StreamIt 4 | // 5 | // Created by Thibault Wittemberg on 14/04/2016. 6 | // Copyright © 2016 Thibault Wittemberg. All rights reserved. 7 | // 8 | 9 | import AVFoundation 10 | import CocoaAsyncSocket 11 | import UIKit 12 | 13 | final class CameraViewController: UIViewController { 14 | 15 | @IBOutlet fileprivate weak var cameraView: UIView! 16 | @IBOutlet fileprivate weak var plusLabel: UILabel! 17 | @IBOutlet fileprivate weak var minusLabel: UILabel! 18 | @IBOutlet fileprivate weak var zoomSlider: UISlider! 19 | @IBOutlet fileprivate weak var ledImage: UIImageView! 20 | @IBOutlet fileprivate weak var informationLabel: UILabel! 21 | 22 | fileprivate let ip = IPChecker.getIP() 23 | fileprivate let captureSession = AVCaptureSession() 24 | fileprivate var previewLayer: AVCaptureVideoPreviewLayer? 25 | fileprivate var captureDevice: AVCaptureDevice? 26 | fileprivate let videoOutput = AVCaptureVideoDataOutput() 27 | fileprivate var clients = [Int: StreamingSession]() 28 | fileprivate var serverSocket: GCDAsyncSocket? 29 | fileprivate var previousOrientation = UIDeviceOrientation.unknown 30 | fileprivate var ipIsDisplayed = false 31 | fileprivate var ipAddress = "" 32 | 33 | fileprivate let serverQueue = DispatchQueue(label: "ServerQueue", attributes: []) 34 | fileprivate let clientQueue = DispatchQueue(label: "ClientQueue", attributes: .concurrent) 35 | fileprivate let socketWriteQueue = DispatchQueue(label: "SocketWriteQueue", attributes: .concurrent) 36 | 37 | // MARK: - Lifecycle 38 | 39 | override func viewDidLoad() { 40 | super.viewDidLoad() 41 | 42 | // on crée la socket de service 43 | // Le serveur tourne dans sa propre queue (les méthodes de délégate seront exécutées dans cette queue) 44 | // Les clients possèdent également leur propre queue dexécution 45 | print("Création du serveur sur l'IP \(String(describing: self.ip))") 46 | self.serverSocket = GCDAsyncSocket(delegate: self, delegateQueue: self.serverQueue, socketQueue: self.socketWriteQueue) 47 | 48 | do { 49 | try self.serverSocket?.accept(onInterface: self.ip, port: 10001) 50 | } catch { 51 | print("Could not start listening on port 10001 (\(error))") 52 | } 53 | 54 | // Do any additional setup after loading the view. 55 | self.captureSession.sessionPreset = .medium 56 | let devices = AVCaptureDevice.devices() 57 | 58 | for device in devices { 59 | if device.hasMediaType(.video) { 60 | self.captureDevice = device 61 | if captureDevice != nil { 62 | print("Capture device found") 63 | beginSession() 64 | break 65 | } 66 | } 67 | } 68 | 69 | if let ip = self.ip { 70 | ipAddress = "http://\(ip):10001" 71 | } else { 72 | ipAddress = "IP address not available" 73 | } 74 | 75 | self.cameraView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(CameraViewController.tapOnCameraView))) 76 | } 77 | 78 | // MARK: - Actions 79 | 80 | @objc fileprivate func tapOnCameraView() { 81 | 82 | UIView.animate(withDuration: 1, animations: { [unowned self] in 83 | if self.ipIsDisplayed { 84 | self.informationLabel.alpha = 0 85 | } else { 86 | self.informationLabel.alpha = 1 87 | self.informationLabel.text = self.ipAddress 88 | } 89 | 90 | self.ipIsDisplayed = !self.ipIsDisplayed 91 | }) 92 | } 93 | 94 | @IBAction fileprivate func zoomChanged(_ sender: UISlider, forEvent event: UIEvent) { 95 | do { 96 | try self.captureDevice?.lockForConfiguration() 97 | self.captureDevice?.videoZoomFactor = CGFloat(sender.value) 98 | self.captureDevice?.unlockForConfiguration() 99 | } catch { 100 | print("Could not lock configuration for capture device (\(error))") 101 | } 102 | } 103 | 104 | // MARK: - Helpers 105 | 106 | fileprivate func beginSession() { 107 | do { 108 | guard let captureDevice = self.captureDevice else { 109 | print("Could not find a capture device") 110 | return 111 | } 112 | 113 | try captureDevice.lockForConfiguration() 114 | captureDevice.focusMode = .continuousAutoFocus 115 | captureDevice.unlockForConfiguration() 116 | 117 | let maxZoom = captureDevice.activeFormat.videoMaxZoomFactor 118 | self.zoomSlider.maximumValue = Float(maxZoom) / 2 119 | 120 | try self.captureSession.addInput(AVCaptureDeviceInput(device: captureDevice)) 121 | self.previewLayer = AVCaptureVideoPreviewLayer(session: self.captureSession) 122 | guard let previewLayer = self.previewLayer else { 123 | print("Could not create a preview layer for session") 124 | return 125 | } 126 | 127 | let bounds = self.view.bounds 128 | 129 | previewLayer.bounds = CGRect(origin: CGPoint.zero, size: CGSize(width: bounds.width, height: bounds.height)) 130 | previewLayer.videoGravity = .resize 131 | previewLayer.position = CGPoint(x: bounds.midX, y: bounds.midY) 132 | 133 | videoOutput.setSampleBufferDelegate(self, queue: DispatchQueue(label: "AVSessionQueue", attributes: [])) 134 | self.captureSession.addOutput(videoOutput) 135 | self.cameraView.layer.addSublayer(previewLayer) 136 | 137 | //self.previewLayer?.frame = self.view.layer.frame 138 | self.captureSession.startRunning() 139 | } catch { 140 | print("Could not begin a capture session (\(error))") 141 | } 142 | } 143 | 144 | let context = CIContext(options: nil) 145 | 146 | fileprivate func rotateLabels(_ angle: CGFloat) { 147 | DispatchQueue.main.async(execute: { 148 | UIView.animate(withDuration: 0.5, animations: { 149 | self.minusLabel.transform = CGAffineTransform.identity.rotated(by: angle) 150 | self.plusLabel.transform = CGAffineTransform.identity.rotated(by: angle) 151 | self.zoomSlider.transform = CGAffineTransform.identity.rotated(by: CGFloat(0)) 152 | self.informationLabel.transform = CGAffineTransform.identity.rotated(by: angle) 153 | }) 154 | }) 155 | } 156 | 157 | fileprivate func switchLabels() { 158 | DispatchQueue.main.async(execute: { 159 | UIView.animate(withDuration: 0.5, animations: { 160 | let c1 = self.minusLabel.center 161 | let c2 = self.plusLabel.center 162 | 163 | let dx = c2.x - c1.x 164 | let dy = c2.y - c1.y 165 | 166 | self.minusLabel.transform = CGAffineTransform.identity.translatedBy(x: dx, y: dy) 167 | self.plusLabel.transform = CGAffineTransform.identity.translatedBy(x: -dx, y: -dy) 168 | 169 | self.minusLabel.transform = self.minusLabel.transform.rotated(by: CGFloat(-1/2 * Double.pi)) 170 | self.plusLabel.transform = self.plusLabel.transform.rotated(by: CGFloat(-1/2 * Double.pi)) 171 | 172 | self.zoomSlider.transform = CGAffineTransform.identity.rotated(by: CGFloat(Double.pi)) 173 | 174 | self.informationLabel.transform = CGAffineTransform.identity.rotated(by: CGFloat(-1/2 * Double.pi)) 175 | 176 | }) 177 | }) 178 | } 179 | 180 | fileprivate func updateOrientation() { 181 | let currentOrientation = UIDevice.current.orientation 182 | if currentOrientation != self.previousOrientation { 183 | switch currentOrientation { 184 | case .portrait: 185 | self.videoOutput.connection(with: .video)?.videoOrientation = .portrait 186 | self.rotateLabels(0) 187 | case .landscapeRight: 188 | self.videoOutput.connection(with: .video)?.videoOrientation = .landscapeLeft 189 | self.switchLabels() 190 | case .landscapeLeft: 191 | self.videoOutput.connection(with: .video)?.videoOrientation = .landscapeRight 192 | self.rotateLabels(CGFloat(1/2 * Double.pi)) 193 | case .portraitUpsideDown: 194 | self.videoOutput.connection(with: .video)?.videoOrientation = .portraitUpsideDown 195 | self.rotateLabels(0) 196 | default: 197 | self.videoOutput.connection(with: .video)?.videoOrientation = .portrait 198 | self.rotateLabels(0) 199 | } 200 | 201 | self.previousOrientation = currentOrientation 202 | } 203 | } 204 | } 205 | 206 | // MARK: - AVCaptureVideoDataOutputSampleBufferDelegate 207 | 208 | extension CameraViewController: AVCaptureVideoDataOutputSampleBufferDelegate { 209 | 210 | func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) { 211 | self.updateOrientation() 212 | 213 | if !self.clients.isEmpty { 214 | guard let capture: CVImageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return } 215 | let sourceImage = CIImage(cvImageBuffer: capture, options: nil) 216 | guard let tempImage = self.context.createCGImage(sourceImage, from: sourceImage.extent) else { return } 217 | let image = UIImage(cgImage: tempImage) 218 | let imageToSend = image.jpegData(compressionQuality: 0) 219 | 220 | for (key, client) in self.clients { 221 | if client.connected { 222 | client.dataToSend = (imageToSend as NSData?)?.copy() as? Data 223 | } else { 224 | self.clients.removeValue(forKey: key) 225 | } 226 | } 227 | 228 | if self.clients.isEmpty { 229 | DispatchQueue.main.async(execute: { 230 | self.ledImage.image = UIImage(named: "led_gray") 231 | }) 232 | } 233 | } 234 | } 235 | } 236 | 237 | // MARK: - GCDAsyncSocketDelegate 238 | 239 | extension CameraViewController: GCDAsyncSocketDelegate { 240 | 241 | func socket(_ sock: GCDAsyncSocket, didAcceptNewSocket newSocket: GCDAsyncSocket) { 242 | print("New client from IP \(newSocket.connectedHost ?? "unknown")") 243 | guard let clientId = newSocket.connectedAddress?.hashValue else { return } 244 | 245 | let newClient = StreamingSession(id: clientId, client: newSocket, queue: self.clientQueue) 246 | self.clients[clientId] = newClient 247 | newClient.startStreaming() 248 | 249 | DispatchQueue.main.async(execute: { 250 | self.ledImage.image = UIImage(named: "led_red") 251 | }) 252 | } 253 | } 254 | -------------------------------------------------------------------------------- /StreamIt/IPChecker/IPChecker.h: -------------------------------------------------------------------------------- 1 | // 2 | // IPChecker.h 3 | // anus 4 | // 5 | // Created by Thibault Wittemberg on 05/04/2016. 6 | // Copyright © 2016 Thibault Wittemberg. All rights reserved. 7 | // 8 | 9 | #ifndef IPChecker_h 10 | #define IPChecker_h 11 | 12 | 13 | #endif /* IPChecker_h */ 14 | 15 | #import 16 | 17 | @interface IPChecker : NSObject 18 | 19 | + (NSString *)getIP; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /StreamIt/IPChecker/IPChecker.m: -------------------------------------------------------------------------------- 1 | // 2 | // IPChecker.m 3 | // anus 4 | // 5 | // Created by Thibault Wittemberg on 05/04/2016. 6 | // Copyright © 2016 Thibault Wittemberg. All rights reserved. 7 | // 8 | 9 | #import 10 | #include 11 | #include 12 | #include "IPChecker.h" 13 | 14 | @implementation IPChecker 15 | 16 | + (NSString *)getIP 17 | { 18 | NSString *address = @"ip_address_error"; 19 | struct ifaddrs *interfaces = NULL; 20 | struct ifaddrs *addr = NULL; 21 | int got = getifaddrs(&interfaces); 22 | if (got == 0) { 23 | addr = interfaces; 24 | while (addr != NULL) { 25 | if( addr->ifa_addr->sa_family == AF_INET) { 26 | if ([[NSString stringWithUTF8String:addr->ifa_name] isEqualToString:@"en0"]) { 27 | address = [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)addr->ifa_addr)->sin_addr)]; 28 | } 29 | } 30 | 31 | addr = addr->ifa_next; 32 | } 33 | } 34 | 35 | freeifaddrs(interfaces); 36 | 37 | return address; 38 | } 39 | @end 40 | -------------------------------------------------------------------------------- /StreamIt/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | NSAppTransportSecurity 26 | 27 | NSAllowsArbitraryLoads 28 | 29 | 30 | NSCameraUsageDescription 31 | $(PRODUCT_NAME) photo use 32 | UILaunchStoryboardName 33 | LaunchScreen 34 | UIMainStoryboardFile 35 | Main 36 | UIRequiredDeviceCapabilities 37 | 38 | armv7 39 | 40 | UIRequiresFullScreen 41 | 42 | UIStatusBarHidden 43 | 44 | UIStatusBarStyle 45 | UIStatusBarStyleLightContent 46 | UISupportedInterfaceOrientations 47 | 48 | UIInterfaceOrientationPortrait 49 | 50 | UISupportedInterfaceOrientations~ipad 51 | 52 | UIInterfaceOrientationPortrait 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /StreamIt/Queue.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Queue.swift 3 | // StreamIt 4 | // 5 | // Created by Thibault Wittemberg on 14/04/2016. 6 | // Copyright © 2016 Thibault Wittemberg. All rights reserved. 7 | // 8 | 9 | private class QueueItem { 10 | 11 | fileprivate let value: T! 12 | fileprivate var next: QueueItem? 13 | 14 | init(_ newvalue: T?) { 15 | self.value = newvalue 16 | } 17 | } 18 | 19 | open class Queue { 20 | 21 | fileprivate var _front: QueueItem 22 | fileprivate var _back: QueueItem 23 | fileprivate var maxCapacity: Int 24 | fileprivate var currentSize = 0 25 | 26 | public init(maxCapacity: Int) { 27 | // Insert dummy item. Will disappear when the first item is added. 28 | _back = QueueItem(nil) 29 | _front = _back 30 | self.maxCapacity = maxCapacity 31 | } 32 | 33 | /// Add a new item to the back of the queue. 34 | open func enqueue(_ value: T) { 35 | if self.currentSize >= maxCapacity { 36 | _back = QueueItem(value) 37 | } else { 38 | _back.next = QueueItem(value) 39 | _back = _back.next! 40 | self.currentSize += 1 41 | } 42 | } 43 | 44 | /// Return and remove the item at the front of the queue. 45 | open func dequeue() -> T? { 46 | if let newhead = _front.next { 47 | _front = newhead 48 | self.currentSize -= 1 49 | return newhead.value 50 | } else { 51 | self.currentSize = 0 52 | return nil 53 | } 54 | } 55 | 56 | open func isEmpty() -> Bool { 57 | return _front === _back 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /StreamIt/StreamIt-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | #import "IPChecker.h" 6 | -------------------------------------------------------------------------------- /StreamIt/StreamingSession.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StreamingSession.swift 3 | // StreamIt 4 | // 5 | // Created by Thibault Wittemberg on 14/04/2016. 6 | // Copyright © 2016 Thibault Wittemberg. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import CocoaAsyncSocket 11 | 12 | class StreamingSession { 13 | 14 | fileprivate var client: GCDAsyncSocket 15 | fileprivate var headersSent = false 16 | fileprivate var dataStack = Queue(maxCapacity: 1) 17 | fileprivate var queue: DispatchQueue 18 | fileprivate let footersData = ["", ""].joined(separator: "\r\n").data(using: String.Encoding.utf8) 19 | 20 | var id: Int 21 | var connected = true 22 | var dataToSend: Data? { 23 | didSet { 24 | guard let dataToSend = self.dataToSend else { return } 25 | 26 | self.dataStack.enqueue(dataToSend) 27 | } 28 | } 29 | 30 | // MARK: - Lifecycle 31 | 32 | init (id: Int, client: GCDAsyncSocket, queue: DispatchQueue) { 33 | print("Creating client [#\(id)]") 34 | 35 | self.id = id 36 | self.client = client 37 | self.queue = queue 38 | } 39 | 40 | // MARK: - Methods 41 | 42 | func close() { 43 | print("Closing client [#\(self.id)]") 44 | 45 | self.connected = false 46 | } 47 | 48 | func startStreaming() { 49 | self.queue.async(execute: { [unowned self] in 50 | while self.connected { 51 | 52 | if !self.headersSent { 53 | print("Sending headers [#\(self.id)]") 54 | 55 | let headers = [ 56 | "HTTP/1.0 200 OK", 57 | "Connection: keep-alive", 58 | "Ma-age: 0", 59 | "Expires: 0", 60 | "Cache-Control: no-store,must-revalidate", 61 | "Access-Control-Allow-Origin: *", 62 | "Access-Control-Allow-Headers: accept,content-type", 63 | "Access-Control-Allow-Methods: GET", 64 | "Access-Control-expose-headers: Cache-Control,Content-Encoding", 65 | "Pragma: no-cache", 66 | "Content-type: multipart/x-mixed-replace; boundary=0123456789876543210", 67 | "" 68 | ] 69 | 70 | guard let headersData = headers.joined(separator: "\r\n").data(using: String.Encoding.utf8) else { 71 | print("Could not make headers data [#\(self.id)]") 72 | return 73 | } 74 | 75 | self.headersSent = true 76 | self.client.write(headersData, withTimeout: -1, tag: 0) 77 | } else { 78 | if (self.client.connectedPort.hashValue == 0 || !self.client.isConnected) { 79 | // y a personne en face ... on arrête d'envoyer des données 80 | self.close() 81 | print("Dropping client [#\(self.id)]") 82 | } 83 | 84 | if let data = self.dataStack.dequeue() { 85 | let frameHeaders = [ 86 | "", 87 | "--0123456789876543210", 88 | "Content-Type: image/jpeg", 89 | "Content-Length: \(data.count)", 90 | "", 91 | "" 92 | ] 93 | 94 | guard let frameHeadersData = frameHeaders.joined(separator: "\r\n").data(using: String.Encoding.utf8) else { 95 | print("Could not make frame headers data [#\(self.id)]") 96 | return 97 | } 98 | 99 | self.client.write(frameHeadersData, withTimeout: -1, tag: 0) 100 | self.client.write(data, withTimeout: -1, tag: 0) 101 | self.client.write(self.footersData!, withTimeout: -1, tag: self.id) 102 | } 103 | } 104 | } 105 | }) 106 | } 107 | } 108 | --------------------------------------------------------------------------------