├── .gitignore ├── Podfile ├── Podfile.lock ├── README.md ├── WebRTCHandsOn.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── WebRTCHandsOn.xcworkspace └── contents.xcworkspacedata └── WebRTCHandsOn ├── AppDelegate.swift ├── Base.lproj └── Main.storyboard ├── CONTROLLER ├── CallViewController.swift ├── ChatViewController.swift └── ViewController.swift ├── SERVICE ├── CallManager.swift └── WebRTC.swift ├── Support ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ └── LaunchScreen.storyboard └── Info.plist └── Utility └── Utility.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 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xccheckout 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | 31 | ## Playgrounds 32 | timeline.xctimeline 33 | playground.xcworkspace 34 | 35 | # Swift Package Manager 36 | # 37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 38 | # Packages/ 39 | # Package.pins 40 | .build/ 41 | 42 | # CocoaPods 43 | # 44 | # We recommend against adding the Pods directory to your .gitignore. However 45 | # you should judge for yourself, the pros and cons are mentioned at: 46 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 47 | # 48 | Pods/ 49 | 50 | # Carthage 51 | # 52 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 53 | # Carthage/Checkouts 54 | 55 | Carthage/Build 56 | 57 | # fastlane 58 | # 59 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 60 | # screenshots whenever they are needed. 61 | # For more information about the recommended setup visit: 62 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 63 | 64 | fastlane/report.xml 65 | fastlane/Preview.html 66 | fastlane/screenshots 67 | fastlane/test_output 68 | 69 | 70 | # Firebase GoogleService info file 71 | WebRTCHandsOn/WebRTCHandsOn/Support/GoogleService-Info.plist 72 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment the next line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | target 'WebRTCHandsOn' do 5 | # Comment the next line if you're not using Swift and don't want to use dynamic frameworks 6 | use_frameworks! 7 | 8 | # Pods for WebRTCHandsOn 9 | pod 'Firebase/Core' 10 | pod 'Firebase/Database' 11 | 12 | pod 'GoogleWebRTC' 13 | # pod 'WebRTC', '58.17.16937' 14 | # pod 'Starscream', '~> 2.0.3' 15 | pod 'SwiftyJSON' 16 | end 17 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Firebase/Core (4.8.2): 3 | - FirebaseAnalytics (= 4.0.9) 4 | - FirebaseCore (= 4.0.14) 5 | - Firebase/Database (4.8.2): 6 | - Firebase/Core 7 | - FirebaseDatabase (= 4.1.4) 8 | - FirebaseAnalytics (4.0.9): 9 | - FirebaseCore (~> 4.0) 10 | - FirebaseInstanceID (~> 2.0) 11 | - GoogleToolboxForMac/NSData+zlib (~> 2.1) 12 | - nanopb (~> 0.3) 13 | - FirebaseCore (4.0.14): 14 | - GoogleToolboxForMac/NSData+zlib (~> 2.1) 15 | - FirebaseDatabase (4.1.4): 16 | - FirebaseAnalytics (~> 4.0) 17 | - FirebaseCore (~> 4.0) 18 | - leveldb-library (~> 1.18) 19 | - FirebaseInstanceID (2.0.8): 20 | - FirebaseCore (~> 4.0) 21 | - GoogleToolboxForMac/Defines (2.1.3) 22 | - GoogleToolboxForMac/NSData+zlib (2.1.3): 23 | - GoogleToolboxForMac/Defines (= 2.1.3) 24 | - GoogleWebRTC (1.1.21820) 25 | - leveldb-library (1.20) 26 | - nanopb (0.3.8): 27 | - nanopb/decode (= 0.3.8) 28 | - nanopb/encode (= 0.3.8) 29 | - nanopb/decode (0.3.8) 30 | - nanopb/encode (0.3.8) 31 | - SwiftyJSON (4.0.0) 32 | 33 | DEPENDENCIES: 34 | - Firebase/Core 35 | - Firebase/Database 36 | - GoogleWebRTC 37 | - SwiftyJSON 38 | 39 | SPEC CHECKSUMS: 40 | Firebase: 7d3b8cd837ad9fcd391657734c0d56dab8e9a5a3 41 | FirebaseAnalytics: 388b630c15713f5dbf364071f5f3d6077fb52f4e 42 | FirebaseCore: 2e0b98fb2d64ca8140136beff15772bdd14d2dd7 43 | FirebaseDatabase: de4446507ccd3257fca37d16f40e1540324571fd 44 | FirebaseInstanceID: 81df5805a08001e69138664bdd02c6719a9ac80f 45 | GoogleToolboxForMac: 2501e2ad72a52eb3dfe7bd9aee7dad11b858bd20 46 | GoogleWebRTC: ed58109f4503d84c53471d3797820294794e0836 47 | leveldb-library: 08cba283675b7ed2d99629a4bc5fd052cd2bb6a5 48 | nanopb: 5601e6bca2dbf1ed831b519092ec110f66982ca3 49 | SwiftyJSON: 070dabdcb1beb81b247c65ffa3a79dbbfb3b48aa 50 | 51 | PODFILE CHECKSUM: e16c9b40415eb163da8e0f1ecb80f7d1bf22a008 52 | 53 | COCOAPODS: 1.3.1 54 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SwiftでWebRTC 2 | Clone from [tnoho/WebRTCHandsOn](https://github.com/tnoho/WebRTCHandsOn) 3 | And modify to using Firebase database for SDP signaling 4 | 5 | # Usage 6 | 7 | ### 1. Config your [Firebase project](https://firebase.google.com/docs/database/ios/start?authuser=0) 8 | + Make your database rule allow read/write at `Call/`: 9 | ``` 10 | "Call": { 11 | ".read": true, 12 | ".write": true 13 | } 14 | ``` 15 | ### 2. Build: 16 | you need use 2 device to test conection 17 | + Before build app on first one, make sure 18 | ``` 19 | var sender: Int = 1 20 | var receiver: Int = 2 21 | ``` 22 | in `ChatViewController` 23 | 24 | + And change it when build the second: 25 | ``` 26 | var sender: Int = 2 27 | var receiver: Int = 1 28 | ``` 29 | -------------------------------------------------------------------------------- /WebRTCHandsOn.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 864724BDFA2FD4F613B8302B /* Pods_WebRTCHandsOn.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AF10745FA17AD353468CD464 /* Pods_WebRTCHandsOn.framework */; }; 11 | DF78A882202C495300E49750 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = DF78A881202C495300E49750 /* README.md */; }; 12 | DF78A886202D7E8000E49750 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = DF78A885202D7E8000E49750 /* GoogleService-Info.plist */; }; 13 | DF78A8892032A7AF00E49750 /* Utility.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF78A8882032A7AF00E49750 /* Utility.swift */; }; 14 | DF78A88B2032DF1F00E49750 /* CallViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF78A88A2032DF1F00E49750 /* CallViewController.swift */; }; 15 | DF78A88E2032EDBC00E49750 /* WebRTC.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF78A88D2032EDBC00E49750 /* WebRTC.swift */; }; 16 | DF78A8902032F31B00E49750 /* CallManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF78A88F2032F31B00E49750 /* CallManager.swift */; }; 17 | E93377FA1ED962BB0011FBC1 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E93377F91ED962BB0011FBC1 /* AppDelegate.swift */; }; 18 | E93377FC1ED962BB0011FBC1 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E93377FB1ED962BB0011FBC1 /* ViewController.swift */; }; 19 | E93377FF1ED962BB0011FBC1 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E93377FD1ED962BB0011FBC1 /* Main.storyboard */; }; 20 | E93378011ED962BB0011FBC1 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E93378001ED962BB0011FBC1 /* Assets.xcassets */; }; 21 | E93378041ED962BB0011FBC1 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E93378021ED962BB0011FBC1 /* LaunchScreen.storyboard */; }; 22 | E93829171ED9BFF000FD4E46 /* ChatViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E93829161ED9BFF000FD4E46 /* ChatViewController.swift */; }; 23 | /* End PBXBuildFile section */ 24 | 25 | /* Begin PBXFileReference section */ 26 | 03F38ACDCCCA487C8D52C7EC /* Pods-WebRTCHandsOn.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WebRTCHandsOn.debug.xcconfig"; path = "Pods/Target Support Files/Pods-WebRTCHandsOn/Pods-WebRTCHandsOn.debug.xcconfig"; sourceTree = ""; }; 27 | 28A9AF4405E953428B143A23 /* Pods-WebRTCHandsOn.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WebRTCHandsOn.release.xcconfig"; path = "Pods/Target Support Files/Pods-WebRTCHandsOn/Pods-WebRTCHandsOn.release.xcconfig"; sourceTree = ""; }; 28 | AF10745FA17AD353468CD464 /* Pods_WebRTCHandsOn.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_WebRTCHandsOn.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 29 | DF78A881202C495300E49750 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 30 | DF78A885202D7E8000E49750 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; 31 | DF78A8882032A7AF00E49750 /* Utility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utility.swift; sourceTree = ""; }; 32 | DF78A88A2032DF1F00E49750 /* CallViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallViewController.swift; sourceTree = ""; }; 33 | DF78A88D2032EDBC00E49750 /* WebRTC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebRTC.swift; sourceTree = ""; }; 34 | DF78A88F2032F31B00E49750 /* CallManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallManager.swift; sourceTree = ""; }; 35 | E93377F61ED962BB0011FBC1 /* WebRTCHandsOn.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WebRTCHandsOn.app; sourceTree = BUILT_PRODUCTS_DIR; }; 36 | E93377F91ED962BB0011FBC1 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 37 | E93377FB1ED962BB0011FBC1 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 38 | E93377FE1ED962BB0011FBC1 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 39 | E93378001ED962BB0011FBC1 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 40 | E93378031ED962BB0011FBC1 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 41 | E93378051ED962BB0011FBC1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 42 | E93829161ED9BFF000FD4E46 /* ChatViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChatViewController.swift; sourceTree = ""; }; 43 | /* End PBXFileReference section */ 44 | 45 | /* Begin PBXFrameworksBuildPhase section */ 46 | E93377F31ED962BB0011FBC1 /* Frameworks */ = { 47 | isa = PBXFrameworksBuildPhase; 48 | buildActionMask = 2147483647; 49 | files = ( 50 | 864724BDFA2FD4F613B8302B /* Pods_WebRTCHandsOn.framework in Frameworks */, 51 | ); 52 | runOnlyForDeploymentPostprocessing = 0; 53 | }; 54 | /* End PBXFrameworksBuildPhase section */ 55 | 56 | /* Begin PBXGroup section */ 57 | 20593083E8AB6F86C8D63440 /* Pods */ = { 58 | isa = PBXGroup; 59 | children = ( 60 | 03F38ACDCCCA487C8D52C7EC /* Pods-WebRTCHandsOn.debug.xcconfig */, 61 | 28A9AF4405E953428B143A23 /* Pods-WebRTCHandsOn.release.xcconfig */, 62 | ); 63 | name = Pods; 64 | sourceTree = ""; 65 | }; 66 | A6F0007126D66892EE13215B /* Frameworks */ = { 67 | isa = PBXGroup; 68 | children = ( 69 | AF10745FA17AD353468CD464 /* Pods_WebRTCHandsOn.framework */, 70 | ); 71 | name = Frameworks; 72 | sourceTree = ""; 73 | }; 74 | DF78A883202C4A9700E49750 /* CONTROLLER */ = { 75 | isa = PBXGroup; 76 | children = ( 77 | E93377FB1ED962BB0011FBC1 /* ViewController.swift */, 78 | E93829161ED9BFF000FD4E46 /* ChatViewController.swift */, 79 | DF78A88A2032DF1F00E49750 /* CallViewController.swift */, 80 | ); 81 | path = CONTROLLER; 82 | sourceTree = ""; 83 | }; 84 | DF78A884202C4AB400E49750 /* Support */ = { 85 | isa = PBXGroup; 86 | children = ( 87 | DF78A885202D7E8000E49750 /* GoogleService-Info.plist */, 88 | E93378001ED962BB0011FBC1 /* Assets.xcassets */, 89 | E93378021ED962BB0011FBC1 /* LaunchScreen.storyboard */, 90 | E93378051ED962BB0011FBC1 /* Info.plist */, 91 | ); 92 | path = Support; 93 | sourceTree = ""; 94 | }; 95 | DF78A88720329AE500E49750 /* Utility */ = { 96 | isa = PBXGroup; 97 | children = ( 98 | DF78A8882032A7AF00E49750 /* Utility.swift */, 99 | ); 100 | path = Utility; 101 | sourceTree = ""; 102 | }; 103 | DF78A88C2032ED9C00E49750 /* SERVICE */ = { 104 | isa = PBXGroup; 105 | children = ( 106 | DF78A88D2032EDBC00E49750 /* WebRTC.swift */, 107 | DF78A88F2032F31B00E49750 /* CallManager.swift */, 108 | ); 109 | path = SERVICE; 110 | sourceTree = ""; 111 | }; 112 | E93377ED1ED962BB0011FBC1 = { 113 | isa = PBXGroup; 114 | children = ( 115 | DF78A881202C495300E49750 /* README.md */, 116 | E93377F81ED962BB0011FBC1 /* WebRTCHandsOn */, 117 | E93377F71ED962BB0011FBC1 /* Products */, 118 | 20593083E8AB6F86C8D63440 /* Pods */, 119 | A6F0007126D66892EE13215B /* Frameworks */, 120 | ); 121 | sourceTree = ""; 122 | }; 123 | E93377F71ED962BB0011FBC1 /* Products */ = { 124 | isa = PBXGroup; 125 | children = ( 126 | E93377F61ED962BB0011FBC1 /* WebRTCHandsOn.app */, 127 | ); 128 | name = Products; 129 | sourceTree = ""; 130 | }; 131 | E93377F81ED962BB0011FBC1 /* WebRTCHandsOn */ = { 132 | isa = PBXGroup; 133 | children = ( 134 | E93377F91ED962BB0011FBC1 /* AppDelegate.swift */, 135 | E93377FD1ED962BB0011FBC1 /* Main.storyboard */, 136 | DF78A883202C4A9700E49750 /* CONTROLLER */, 137 | DF78A88C2032ED9C00E49750 /* SERVICE */, 138 | DF78A88720329AE500E49750 /* Utility */, 139 | DF78A884202C4AB400E49750 /* Support */, 140 | ); 141 | path = WebRTCHandsOn; 142 | sourceTree = ""; 143 | }; 144 | /* End PBXGroup section */ 145 | 146 | /* Begin PBXNativeTarget section */ 147 | E93377F51ED962BB0011FBC1 /* WebRTCHandsOn */ = { 148 | isa = PBXNativeTarget; 149 | buildConfigurationList = E93378081ED962BB0011FBC1 /* Build configuration list for PBXNativeTarget "WebRTCHandsOn" */; 150 | buildPhases = ( 151 | CDDD50B796CFF6C57BD56779 /* [CP] Check Pods Manifest.lock */, 152 | E93377F21ED962BB0011FBC1 /* Sources */, 153 | E93377F31ED962BB0011FBC1 /* Frameworks */, 154 | E93377F41ED962BB0011FBC1 /* Resources */, 155 | 2F4868832D0C299423BF864B /* [CP] Embed Pods Frameworks */, 156 | 321D6E355BE68047E02369F9 /* [CP] Copy Pods Resources */, 157 | ); 158 | buildRules = ( 159 | ); 160 | dependencies = ( 161 | ); 162 | name = WebRTCHandsOn; 163 | productName = WebRTCHandsOn; 164 | productReference = E93377F61ED962BB0011FBC1 /* WebRTCHandsOn.app */; 165 | productType = "com.apple.product-type.application"; 166 | }; 167 | /* End PBXNativeTarget section */ 168 | 169 | /* Begin PBXProject section */ 170 | E93377EE1ED962BB0011FBC1 /* Project object */ = { 171 | isa = PBXProject; 172 | attributes = { 173 | LastSwiftUpdateCheck = 0830; 174 | LastUpgradeCheck = 0920; 175 | ORGANIZATIONNAME = tnoho; 176 | TargetAttributes = { 177 | E93377F51ED962BB0011FBC1 = { 178 | CreatedOnToolsVersion = 8.3.2; 179 | DevelopmentTeam = 6NE57FX5TR; 180 | LastSwiftMigration = 0920; 181 | ProvisioningStyle = Automatic; 182 | }; 183 | }; 184 | }; 185 | buildConfigurationList = E93377F11ED962BB0011FBC1 /* Build configuration list for PBXProject "WebRTCHandsOn" */; 186 | compatibilityVersion = "Xcode 3.2"; 187 | developmentRegion = English; 188 | hasScannedForEncodings = 0; 189 | knownRegions = ( 190 | en, 191 | Base, 192 | ); 193 | mainGroup = E93377ED1ED962BB0011FBC1; 194 | productRefGroup = E93377F71ED962BB0011FBC1 /* Products */; 195 | projectDirPath = ""; 196 | projectRoot = ""; 197 | targets = ( 198 | E93377F51ED962BB0011FBC1 /* WebRTCHandsOn */, 199 | ); 200 | }; 201 | /* End PBXProject section */ 202 | 203 | /* Begin PBXResourcesBuildPhase section */ 204 | E93377F41ED962BB0011FBC1 /* Resources */ = { 205 | isa = PBXResourcesBuildPhase; 206 | buildActionMask = 2147483647; 207 | files = ( 208 | E93378041ED962BB0011FBC1 /* LaunchScreen.storyboard in Resources */, 209 | DF78A882202C495300E49750 /* README.md in Resources */, 210 | E93378011ED962BB0011FBC1 /* Assets.xcassets in Resources */, 211 | E93377FF1ED962BB0011FBC1 /* Main.storyboard in Resources */, 212 | DF78A886202D7E8000E49750 /* GoogleService-Info.plist in Resources */, 213 | ); 214 | runOnlyForDeploymentPostprocessing = 0; 215 | }; 216 | /* End PBXResourcesBuildPhase section */ 217 | 218 | /* Begin PBXShellScriptBuildPhase section */ 219 | 2F4868832D0C299423BF864B /* [CP] Embed Pods Frameworks */ = { 220 | isa = PBXShellScriptBuildPhase; 221 | buildActionMask = 2147483647; 222 | files = ( 223 | ); 224 | inputPaths = ( 225 | "${SRCROOT}/Pods/Target Support Files/Pods-WebRTCHandsOn/Pods-WebRTCHandsOn-frameworks.sh", 226 | "${BUILT_PRODUCTS_DIR}/GoogleToolboxForMac/GoogleToolboxForMac.framework", 227 | "${PODS_ROOT}/GoogleWebRTC/Frameworks/frameworks/WebRTC.framework", 228 | "${BUILT_PRODUCTS_DIR}/SwiftyJSON/SwiftyJSON.framework", 229 | "${BUILT_PRODUCTS_DIR}/leveldb-library/leveldb.framework", 230 | "${BUILT_PRODUCTS_DIR}/nanopb/nanopb.framework", 231 | ); 232 | name = "[CP] Embed Pods Frameworks"; 233 | outputPaths = ( 234 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleToolboxForMac.framework", 235 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/WebRTC.framework", 236 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftyJSON.framework", 237 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/leveldb.framework", 238 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/nanopb.framework", 239 | ); 240 | runOnlyForDeploymentPostprocessing = 0; 241 | shellPath = /bin/sh; 242 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-WebRTCHandsOn/Pods-WebRTCHandsOn-frameworks.sh\"\n"; 243 | showEnvVarsInLog = 0; 244 | }; 245 | 321D6E355BE68047E02369F9 /* [CP] Copy Pods Resources */ = { 246 | isa = PBXShellScriptBuildPhase; 247 | buildActionMask = 2147483647; 248 | files = ( 249 | ); 250 | inputPaths = ( 251 | ); 252 | name = "[CP] Copy Pods Resources"; 253 | outputPaths = ( 254 | ); 255 | runOnlyForDeploymentPostprocessing = 0; 256 | shellPath = /bin/sh; 257 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-WebRTCHandsOn/Pods-WebRTCHandsOn-resources.sh\"\n"; 258 | showEnvVarsInLog = 0; 259 | }; 260 | CDDD50B796CFF6C57BD56779 /* [CP] Check Pods Manifest.lock */ = { 261 | isa = PBXShellScriptBuildPhase; 262 | buildActionMask = 2147483647; 263 | files = ( 264 | ); 265 | inputPaths = ( 266 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 267 | "${PODS_ROOT}/Manifest.lock", 268 | ); 269 | name = "[CP] Check Pods Manifest.lock"; 270 | outputPaths = ( 271 | "$(DERIVED_FILE_DIR)/Pods-WebRTCHandsOn-checkManifestLockResult.txt", 272 | ); 273 | runOnlyForDeploymentPostprocessing = 0; 274 | shellPath = /bin/sh; 275 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/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# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 276 | showEnvVarsInLog = 0; 277 | }; 278 | /* End PBXShellScriptBuildPhase section */ 279 | 280 | /* Begin PBXSourcesBuildPhase section */ 281 | E93377F21ED962BB0011FBC1 /* Sources */ = { 282 | isa = PBXSourcesBuildPhase; 283 | buildActionMask = 2147483647; 284 | files = ( 285 | DF78A8892032A7AF00E49750 /* Utility.swift in Sources */, 286 | E93377FC1ED962BB0011FBC1 /* ViewController.swift in Sources */, 287 | E93829171ED9BFF000FD4E46 /* ChatViewController.swift in Sources */, 288 | DF78A8902032F31B00E49750 /* CallManager.swift in Sources */, 289 | DF78A88B2032DF1F00E49750 /* CallViewController.swift in Sources */, 290 | DF78A88E2032EDBC00E49750 /* WebRTC.swift in Sources */, 291 | E93377FA1ED962BB0011FBC1 /* AppDelegate.swift in Sources */, 292 | ); 293 | runOnlyForDeploymentPostprocessing = 0; 294 | }; 295 | /* End PBXSourcesBuildPhase section */ 296 | 297 | /* Begin PBXVariantGroup section */ 298 | E93377FD1ED962BB0011FBC1 /* Main.storyboard */ = { 299 | isa = PBXVariantGroup; 300 | children = ( 301 | E93377FE1ED962BB0011FBC1 /* Base */, 302 | ); 303 | name = Main.storyboard; 304 | sourceTree = ""; 305 | }; 306 | E93378021ED962BB0011FBC1 /* LaunchScreen.storyboard */ = { 307 | isa = PBXVariantGroup; 308 | children = ( 309 | E93378031ED962BB0011FBC1 /* Base */, 310 | ); 311 | name = LaunchScreen.storyboard; 312 | sourceTree = ""; 313 | }; 314 | /* End PBXVariantGroup section */ 315 | 316 | /* Begin XCBuildConfiguration section */ 317 | E93378061ED962BB0011FBC1 /* Debug */ = { 318 | isa = XCBuildConfiguration; 319 | buildSettings = { 320 | ALWAYS_SEARCH_USER_PATHS = NO; 321 | CLANG_ANALYZER_NONNULL = YES; 322 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 323 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 324 | CLANG_CXX_LIBRARY = "libc++"; 325 | CLANG_ENABLE_MODULES = YES; 326 | CLANG_ENABLE_OBJC_ARC = YES; 327 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 328 | CLANG_WARN_BOOL_CONVERSION = YES; 329 | CLANG_WARN_COMMA = YES; 330 | CLANG_WARN_CONSTANT_CONVERSION = YES; 331 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 332 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 333 | CLANG_WARN_EMPTY_BODY = YES; 334 | CLANG_WARN_ENUM_CONVERSION = YES; 335 | CLANG_WARN_INFINITE_RECURSION = YES; 336 | CLANG_WARN_INT_CONVERSION = YES; 337 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 338 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 339 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 340 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 341 | CLANG_WARN_STRICT_PROTOTYPES = YES; 342 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 343 | CLANG_WARN_UNREACHABLE_CODE = YES; 344 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 345 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 346 | COPY_PHASE_STRIP = NO; 347 | DEBUG_INFORMATION_FORMAT = dwarf; 348 | ENABLE_STRICT_OBJC_MSGSEND = YES; 349 | ENABLE_TESTABILITY = YES; 350 | GCC_C_LANGUAGE_STANDARD = gnu99; 351 | GCC_DYNAMIC_NO_PIC = NO; 352 | GCC_NO_COMMON_BLOCKS = YES; 353 | GCC_OPTIMIZATION_LEVEL = 0; 354 | GCC_PREPROCESSOR_DEFINITIONS = ( 355 | "DEBUG=1", 356 | "$(inherited)", 357 | ); 358 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 359 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 360 | GCC_WARN_UNDECLARED_SELECTOR = YES; 361 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 362 | GCC_WARN_UNUSED_FUNCTION = YES; 363 | GCC_WARN_UNUSED_VARIABLE = YES; 364 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 365 | MTL_ENABLE_DEBUG_INFO = YES; 366 | ONLY_ACTIVE_ARCH = YES; 367 | SDKROOT = iphoneos; 368 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 369 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 370 | TARGETED_DEVICE_FAMILY = "1,2"; 371 | }; 372 | name = Debug; 373 | }; 374 | E93378071ED962BB0011FBC1 /* Release */ = { 375 | isa = XCBuildConfiguration; 376 | buildSettings = { 377 | ALWAYS_SEARCH_USER_PATHS = NO; 378 | CLANG_ANALYZER_NONNULL = YES; 379 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 380 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 381 | CLANG_CXX_LIBRARY = "libc++"; 382 | CLANG_ENABLE_MODULES = YES; 383 | CLANG_ENABLE_OBJC_ARC = YES; 384 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 385 | CLANG_WARN_BOOL_CONVERSION = YES; 386 | CLANG_WARN_COMMA = YES; 387 | CLANG_WARN_CONSTANT_CONVERSION = YES; 388 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 389 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 390 | CLANG_WARN_EMPTY_BODY = YES; 391 | CLANG_WARN_ENUM_CONVERSION = YES; 392 | CLANG_WARN_INFINITE_RECURSION = YES; 393 | CLANG_WARN_INT_CONVERSION = YES; 394 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 395 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 396 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 397 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 398 | CLANG_WARN_STRICT_PROTOTYPES = YES; 399 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 400 | CLANG_WARN_UNREACHABLE_CODE = YES; 401 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 402 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 403 | COPY_PHASE_STRIP = NO; 404 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 405 | ENABLE_NS_ASSERTIONS = NO; 406 | ENABLE_STRICT_OBJC_MSGSEND = YES; 407 | GCC_C_LANGUAGE_STANDARD = gnu99; 408 | GCC_NO_COMMON_BLOCKS = YES; 409 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 410 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 411 | GCC_WARN_UNDECLARED_SELECTOR = YES; 412 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 413 | GCC_WARN_UNUSED_FUNCTION = YES; 414 | GCC_WARN_UNUSED_VARIABLE = YES; 415 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 416 | MTL_ENABLE_DEBUG_INFO = NO; 417 | SDKROOT = iphoneos; 418 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 419 | TARGETED_DEVICE_FAMILY = "1,2"; 420 | VALIDATE_PRODUCT = YES; 421 | }; 422 | name = Release; 423 | }; 424 | E93378091ED962BB0011FBC1 /* Debug */ = { 425 | isa = XCBuildConfiguration; 426 | baseConfigurationReference = 03F38ACDCCCA487C8D52C7EC /* Pods-WebRTCHandsOn.debug.xcconfig */; 427 | buildSettings = { 428 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 429 | DEVELOPMENT_TEAM = 6NE57FX5TR; 430 | ENABLE_BITCODE = NO; 431 | INFOPLIST_FILE = "$(SRCROOT)/WebRTCHandsOn/Support/Info.plist"; 432 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 433 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 434 | PRODUCT_BUNDLE_IDENTIFIER = DangApp.RaoVatJPDev; 435 | PRODUCT_NAME = "$(TARGET_NAME)"; 436 | SWIFT_SWIFT3_OBJC_INFERENCE = On; 437 | SWIFT_VERSION = 4.0; 438 | }; 439 | name = Debug; 440 | }; 441 | E933780A1ED962BB0011FBC1 /* Release */ = { 442 | isa = XCBuildConfiguration; 443 | baseConfigurationReference = 28A9AF4405E953428B143A23 /* Pods-WebRTCHandsOn.release.xcconfig */; 444 | buildSettings = { 445 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 446 | DEVELOPMENT_TEAM = 6NE57FX5TR; 447 | ENABLE_BITCODE = NO; 448 | INFOPLIST_FILE = "$(SRCROOT)/WebRTCHandsOn/Support/Info.plist"; 449 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 450 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 451 | PRODUCT_BUNDLE_IDENTIFIER = DangApp.RaoVatJPDev; 452 | PRODUCT_NAME = "$(TARGET_NAME)"; 453 | SWIFT_SWIFT3_OBJC_INFERENCE = On; 454 | SWIFT_VERSION = 4.0; 455 | }; 456 | name = Release; 457 | }; 458 | /* End XCBuildConfiguration section */ 459 | 460 | /* Begin XCConfigurationList section */ 461 | E93377F11ED962BB0011FBC1 /* Build configuration list for PBXProject "WebRTCHandsOn" */ = { 462 | isa = XCConfigurationList; 463 | buildConfigurations = ( 464 | E93378061ED962BB0011FBC1 /* Debug */, 465 | E93378071ED962BB0011FBC1 /* Release */, 466 | ); 467 | defaultConfigurationIsVisible = 0; 468 | defaultConfigurationName = Release; 469 | }; 470 | E93378081ED962BB0011FBC1 /* Build configuration list for PBXNativeTarget "WebRTCHandsOn" */ = { 471 | isa = XCConfigurationList; 472 | buildConfigurations = ( 473 | E93378091ED962BB0011FBC1 /* Debug */, 474 | E933780A1ED962BB0011FBC1 /* Release */, 475 | ); 476 | defaultConfigurationIsVisible = 0; 477 | defaultConfigurationName = Release; 478 | }; 479 | /* End XCConfigurationList section */ 480 | }; 481 | rootObject = E93377EE1ED962BB0011FBC1 /* Project object */; 482 | } 483 | -------------------------------------------------------------------------------- /WebRTCHandsOn.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /WebRTCHandsOn.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /WebRTCHandsOn/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // WebRTCHandsOn 4 | // 5 | // Created by Takumi Minamoto on 2017/05/27. 6 | // Copyright © 2017 tnoho. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import WebRTC 11 | import Firebase 12 | 13 | 14 | @UIApplicationMain 15 | class AppDelegate: UIResponder, UIApplicationDelegate { 16 | 17 | var callManager: CallManager? 18 | 19 | var window: UIWindow? 20 | 21 | 22 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 23 | // Override point for customization after application launch. 24 | FirebaseApp.configure() 25 | RTCInitializeSSL() 26 | self.callManager = CallManager() 27 | return true 28 | } 29 | 30 | func applicationWillResignActive(_ application: UIApplication) { 31 | // 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. 32 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 33 | } 34 | 35 | func applicationDidEnterBackground(_ application: UIApplication) { 36 | // 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. 37 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 38 | } 39 | 40 | func applicationWillEnterForeground(_ application: UIApplication) { 41 | // 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. 42 | } 43 | 44 | func applicationDidBecomeActive(_ application: UIApplication) { 45 | // 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. 46 | } 47 | 48 | func applicationWillTerminate(_ application: UIApplication) { 49 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 50 | RTCCleanupSSL() 51 | } 52 | 53 | 54 | } 55 | 56 | -------------------------------------------------------------------------------- /WebRTCHandsOn/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 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 90 | 103 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | -------------------------------------------------------------------------------- /WebRTCHandsOn/CONTROLLER/CallViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CallViewController.swift 3 | // WebRTCHandsOn 4 | // 5 | // Created by dang nguyenhuu on 2018/02/13. 6 | // Copyright © 2018 tnoho. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CallKit 11 | 12 | class CallViewController: UIViewController, CXProviderDelegate { 13 | 14 | override func viewDidLoad() { 15 | 16 | } 17 | 18 | func providerDidReset(_ provider: CXProvider) { 19 | } 20 | 21 | func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) { 22 | action.fulfill() 23 | } 24 | 25 | func provider(_ provider: CXProvider, perform action: CXEndCallAction) { 26 | action.fulfill() 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /WebRTCHandsOn/CONTROLLER/ChatViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChatViewController.swift 3 | // WebRTCHandsOn 4 | // 5 | // Created by Takumi Minamoto on 2017/05/27. 6 | // Copyright © 2017 tnoho. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import WebRTC 11 | import SwiftyJSON 12 | import Firebase 13 | 14 | class ChatViewController: UIViewController { 15 | 16 | var peerConnectionFactory: RTCPeerConnectionFactory! = nil 17 | var peerConnection: RTCPeerConnection! = nil 18 | var remoteVideoTrack: RTCVideoTrack? 19 | var audioSource: RTCAudioSource? 20 | var videoSource: RTCAVFoundationVideoSource? 21 | 22 | var observerSignalRef: DatabaseReference? 23 | var offerSignalRef: DatabaseReference? 24 | 25 | var sender: Int = 1 26 | var receiver: Int = 2 27 | 28 | @IBOutlet weak var cameraPreview: RTCCameraPreviewView! 29 | @IBOutlet weak var remoteVideoView: RTCEAGLVideoView! 30 | 31 | 32 | deinit { 33 | if peerConnection != nil { 34 | hangUp() 35 | } 36 | audioSource = nil 37 | videoSource = nil 38 | peerConnectionFactory = nil 39 | LOG("DEINIT") 40 | } 41 | 42 | 43 | override func viewDidLoad() { 44 | super.viewDidLoad() 45 | 46 | self.remoteVideoView.delegate = self 47 | // RTCPeerConnectionFactoryの初期化 48 | self.peerConnectionFactory = RTCPeerConnectionFactory() 49 | 50 | self.startVideo() 51 | self.setupFirebase() 52 | } 53 | 54 | 55 | func setupFirebase() { 56 | 57 | self.observerSignalRef = Database.database().reference().child("Call/\(receiver)") 58 | self.offerSignalRef = Database.database().reference().child("Call/\(sender)") 59 | 60 | self.offerSignalRef?.onDisconnectRemoveValue() 61 | self.observerSingnal() 62 | } 63 | 64 | 65 | 66 | func observerSingnal() { 67 | 68 | self.observerSignalRef?.observe(.value, with: { [weak self] (snapshot) in 69 | 70 | guard snapshot.exists() else { return } 71 | LOG("message: \(snapshot.value ?? "NO Value")") 72 | 73 | // 受け取ったメッセージをJSONとしてパース 74 | let jsonMessage = JSON(snapshot.value!) 75 | let type = jsonMessage["type"].stringValue 76 | switch (type) { 77 | case "offer": 78 | // offerを受け取った時の処理 79 | LOG("Received offer ...") 80 | let offer = RTCSessionDescription( 81 | type: RTCSessionDescription.type(for: type), 82 | sdp: jsonMessage["sdp"].stringValue) 83 | self?.setOffer(offer) 84 | 85 | case "answer": 86 | // answerを受け取った時の処理 87 | LOG("Received answer ...") 88 | let answer = RTCSessionDescription( 89 | type: RTCSessionDescription.type(for: type), 90 | sdp: jsonMessage["sdp"].stringValue) 91 | self?.setAnswer(answer) 92 | 93 | case "candidate": 94 | LOG("Received ICE candidate ...") 95 | let candidate = RTCIceCandidate( 96 | sdp: jsonMessage["ice"]["candidate"].stringValue, 97 | sdpMLineIndex: jsonMessage["ice"]["sdpMLineIndex"].int32Value, 98 | sdpMid: jsonMessage["ice"]["sdpMid"].stringValue) 99 | self?.addIceCandidate(candidate) 100 | 101 | case "close": 102 | LOG("peer is closed ...") 103 | self?.hangUp() 104 | default: 105 | return 106 | } 107 | }) 108 | 109 | } 110 | 111 | 112 | 113 | 114 | func startVideo() { 115 | // 音声ソースの設定 116 | let audioSourceConstraints = RTCMediaConstraints(mandatoryConstraints: nil, optionalConstraints: nil) 117 | // 音声ソースの生成 118 | audioSource = peerConnectionFactory.audioSource(with: audioSourceConstraints) 119 | 120 | // 映像ソースの設定 121 | let videoSourceConstraints = RTCMediaConstraints(mandatoryConstraints: nil, optionalConstraints: nil) 122 | videoSource = peerConnectionFactory.avFoundationVideoSource(with: videoSourceConstraints) 123 | // 映像ソースをプレビューに設定 124 | cameraPreview.captureSession = videoSource?.captureSession 125 | } 126 | 127 | 128 | func prepareNewConnection() -> RTCPeerConnection { 129 | // STUN/TURNサーバーの指定 130 | let configuration = RTCConfiguration() 131 | configuration.iceServers = [RTCIceServer.init(urlStrings: ["stun:stun.l.google.com:19302", 132 | "stun:stun2.l.google.com:19302", 133 | "stun:stun3.l.google.com:19302", 134 | "stun:stun4.l.google.com:19302"])] 135 | 136 | // PeerConecctionの設定(今回はなし) 137 | let peerConnectionConstraints = RTCMediaConstraints( 138 | mandatoryConstraints: nil, optionalConstraints: nil) 139 | // PeerConnectionの初期化 140 | let peerConnection = peerConnectionFactory.peerConnection(with: configuration, constraints: peerConnectionConstraints, delegate: self) 141 | 142 | // 音声トラックの作成 143 | let localAudioTrack = peerConnectionFactory.audioTrack(with: audioSource!, trackId: "ARDAMSa0") 144 | // PeerConnectionからSenderを作成 145 | let audioSender = peerConnection.sender(withKind: kRTCMediaStreamTrackKindAudio, streamId: "ARDAMS") 146 | // Senderにトラックを設定 147 | audioSender.track = localAudioTrack 148 | 149 | 150 | // 映像トラックの作成 151 | let localVideoTrack = peerConnectionFactory.videoTrack(with: videoSource!, trackId: "ARDAMSv0") 152 | // PeerConnectionからVideoのSenderを作成 153 | let videoSender = peerConnection.sender(withKind: kRTCMediaStreamTrackKindVideo, streamId: "ARDAMS") 154 | // Senderにトラックを設定 155 | videoSender.track = localVideoTrack 156 | 157 | return peerConnection 158 | } 159 | 160 | 161 | @IBAction func connectButtonAction(_ sender: Any) { 162 | // Connectボタンを押した時 163 | if peerConnection == nil { 164 | LOG("make Offer") 165 | makeOffer() 166 | } else { 167 | LOG("peer already exist.") 168 | } 169 | } 170 | 171 | 172 | func makeOffer() { 173 | 174 | peerConnection = prepareNewConnection() // PeerConnectionを生成 175 | 176 | let constraints = RTCMediaConstraints(mandatoryConstraints: ["OfferToReceiveAudio": "true", "OfferToReceiveVideo": "true"], 177 | optionalConstraints: nil) // Offerの設定 今回は映像も音声も受け取る 178 | let offerCompletion = { (offer: RTCSessionDescription?, error: Error?) in // Offerの生成が完了した際の処理 179 | 180 | if error != nil { return } 181 | LOG("createOffer() succsess") 182 | let setLocalDescCompletion = {(error: Error?) in // setLocalDescCompletionが完了した際の処理 183 | 184 | if error != nil { return } 185 | LOG("setLocalDescription() succsess") 186 | 187 | self.sendSDP(offer!) // 相手に送る 188 | } 189 | 190 | self.peerConnection.setLocalDescription(offer!, completionHandler: setLocalDescCompletion) // 生成したOfferを自分のSDPとして設定 191 | } 192 | 193 | 194 | self.peerConnection.offer(for: constraints, completionHandler: offerCompletion) // Offerを生成 195 | } 196 | 197 | 198 | func makeAnswer() { 199 | LOG("sending Answer. Creating remote session description...") 200 | if peerConnection == nil { 201 | LOG("peerConnection NOT exist!") 202 | return 203 | } 204 | 205 | let constraints = RTCMediaConstraints(mandatoryConstraints: nil, optionalConstraints: nil) 206 | let answerCompletion = { (answer: RTCSessionDescription?, error: Error?) in 207 | if error != nil { return } 208 | LOG("createAnswer() succsess") 209 | let setLocalDescCompletion = {(error: Error?) in 210 | if error != nil { return } 211 | LOG("setLocalDescription() succsess") 212 | 213 | self.sendSDP(answer!) // 相手に送る 214 | } 215 | self.peerConnection.setLocalDescription(answer!, completionHandler: setLocalDescCompletion) 216 | } 217 | 218 | self.peerConnection.answer(for: constraints, completionHandler: answerCompletion) // Answerを生成 219 | } 220 | 221 | 222 | func sendSDP(_ desc: RTCSessionDescription) { 223 | LOG("---sending sdp ---") 224 | 225 | let jsonSdp: JSON = [ // JSONを生成 226 | "sdp": desc.sdp, // SDP本体 227 | "type": RTCSessionDescription.string(for: desc.type) // offer か answer か 228 | ] 229 | let message = jsonSdp.dictionaryObject 230 | 231 | self.offerSignalRef?.setValue(message) { (error, ref) in // 相手に送信 232 | if error != nil { 233 | print("Dang sendIceCandidate -->> ", error.debugDescription) 234 | } 235 | } 236 | } 237 | 238 | 239 | func setOffer(_ offer: RTCSessionDescription) { 240 | if peerConnection != nil { 241 | LOG("peerConnection alreay exist!") 242 | } 243 | 244 | // APP_DELGATE.callManager?.receiveCall() 245 | 246 | peerConnection = prepareNewConnection() // PeerConnectionを生成する 247 | self.peerConnection.setRemoteDescription(offer, completionHandler: {(error: Error?) in 248 | if error == nil { 249 | LOG("setRemoteDescription(offer) succsess") 250 | self.makeAnswer() // setRemoteDescriptionが成功したらAnswerを作る 251 | } else { 252 | LOG("setRemoteDescription(offer) ERROR: " + error.debugDescription) 253 | } 254 | }) 255 | } 256 | 257 | 258 | func setAnswer(_ answer: RTCSessionDescription) { 259 | if peerConnection == nil { 260 | LOG("peerConnection NOT exist!") 261 | return 262 | } 263 | 264 | self.peerConnection.setRemoteDescription(answer, completionHandler: { // 受け取ったSDPを相手のSDPとして設定 265 | (error: Error?) in 266 | if error == nil { 267 | LOG("setRemoteDescription(answer) succsess") 268 | } else { 269 | LOG("setRemoteDescription(answer) ERROR: " + error.debugDescription) 270 | } 271 | }) 272 | } 273 | 274 | 275 | func addIceCandidate(_ candidate: RTCIceCandidate) { 276 | if peerConnection != nil { 277 | peerConnection.add(candidate) 278 | } else { 279 | LOG("PeerConnection not exist!") 280 | } 281 | } 282 | 283 | 284 | @IBAction func hangupButtonAction(_ sender: Any) { 285 | 286 | hangUp() // HangUpボタンを押した時 287 | } 288 | 289 | 290 | func hangUp() { 291 | if peerConnection != nil { 292 | if peerConnection.iceConnectionState != RTCIceConnectionState.closed { 293 | peerConnection.close() 294 | let jsonClose: JSON = ["type": "close"] 295 | 296 | let message = jsonClose.dictionaryObject 297 | LOG("sending close message") 298 | let ref = Database.database().reference().child("Call/\(sender)") 299 | ref.setValue(message) { (error, ref) in 300 | print("Dang send SDP Error -->> ", error.debugDescription) 301 | } 302 | 303 | } 304 | if remoteVideoTrack != nil { 305 | remoteVideoTrack?.remove(remoteVideoView) 306 | } 307 | 308 | remoteVideoTrack = nil 309 | peerConnection = nil 310 | LOG("peerConnection is closed.") 311 | } 312 | } 313 | 314 | 315 | @IBAction func closeButtonAction(_ sender: Any) { 316 | // Closeボタンを押した時 317 | hangUp() 318 | _ = self.navigationController?.popToRootViewController(animated: true) 319 | } 320 | 321 | } 322 | 323 | 324 | 325 | 326 | // MARK: - Peer Connection 327 | extension ChatViewController: RTCPeerConnectionDelegate { 328 | 329 | func peerConnection(_ peerConnection: RTCPeerConnection, didChange stateChanged: RTCSignalingState) { 330 | // 接続情報交換の状況が変化した際に呼ばれます 331 | print("\(#function): 接続情報交換の状況が変化した際に呼ばれます") 332 | } 333 | 334 | 335 | func peerConnection(_ peerConnection: RTCPeerConnection, didAdd stream: RTCMediaStream) { 336 | // 映像/音声が追加された際に呼ばれます 337 | LOG("-- peer.onaddstream()") 338 | DispatchQueue.main.async(execute: { () -> Void in 339 | // mainスレッドで実行 340 | if (stream.videoTracks.count > 0) { 341 | // ビデオのトラックを取り出して 342 | self.remoteVideoTrack = stream.videoTracks[0] 343 | // remoteVideoViewに紐づける 344 | self.remoteVideoTrack?.add(self.remoteVideoView) 345 | } 346 | }) 347 | } 348 | 349 | 350 | func peerConnection(_ peerConnection: RTCPeerConnection, didRemove stream: RTCMediaStream) { 351 | // 映像/音声削除された際に呼ばれます 352 | } 353 | 354 | 355 | func peerConnectionShouldNegotiate(_ peerConnection: RTCPeerConnection) { 356 | // 接続情報の交換が必要になった際に呼ばれます 357 | } 358 | 359 | 360 | func peerConnection(_ peerConnection: RTCPeerConnection, didChange newState: RTCIceConnectionState) { 361 | // PeerConnectionの接続状況が変化した際に呼ばれます 362 | var state = "" 363 | switch (newState) { 364 | case RTCIceConnectionState.checking: state = "checking" 365 | case RTCIceConnectionState.completed: state = "completed" 366 | case RTCIceConnectionState.connected: state = "connected" 367 | case RTCIceConnectionState.closed: 368 | state = "closed" 369 | hangUp() 370 | case RTCIceConnectionState.failed: 371 | state = "failed" 372 | hangUp() 373 | case RTCIceConnectionState.disconnected: state = "disconnected" 374 | default: break 375 | } 376 | LOG("ICE connection Status has changed to \(state)") 377 | } 378 | 379 | 380 | func peerConnection(_ peerConnection: RTCPeerConnection, didChange newState: RTCIceGatheringState) { 381 | // 接続先候補の探索状況が変化した際に呼ばれます 382 | } 383 | 384 | 385 | func peerConnection(_ peerConnection: RTCPeerConnection, didGenerate candidate: RTCIceCandidate) { 386 | // Candidate(自分への接続先候補情報)が生成された際に呼ばれます 387 | if candidate.sdpMid != nil { 388 | sendIceCandidate(candidate) 389 | } else { 390 | LOG("empty ice event") 391 | } 392 | } 393 | 394 | 395 | func sendIceCandidate(_ candidate: RTCIceCandidate) { 396 | LOG("---sending ICE candidate ---") 397 | let jsonCandidate: JSON = ["type": "candidate", 398 | "ice": [ 399 | "candidate": candidate.sdp, 400 | "sdpMLineIndex": candidate.sdpMLineIndex, 401 | "sdpMid": candidate.sdpMid! 402 | ] 403 | ] 404 | 405 | let message = jsonCandidate.dictionaryObject 406 | 407 | self.offerSignalRef?.setValue(message) { (error, ref) in 408 | if error != nil { 409 | print("Dang sendIceCandidate -->> ", error.debugDescription) 410 | } 411 | } 412 | } 413 | 414 | 415 | func peerConnection(_ peerConnection: RTCPeerConnection, didOpen dataChannel: RTCDataChannel) { 416 | // DataChannelが作られた際に呼ばれます 417 | } 418 | 419 | 420 | func peerConnection(_ peerConnection: RTCPeerConnection, didRemove candidates: [RTCIceCandidate]) { 421 | // Candidateが削除された際に呼ばれます 422 | } 423 | 424 | } 425 | 426 | 427 | 428 | // MARK: - RTCEAGLVideoViewDelegate 429 | extension ChatViewController: RTCEAGLVideoViewDelegate { 430 | 431 | func videoView(_ videoView: RTCEAGLVideoView, didChangeVideoSize size: CGSize) { 432 | let width = self.view.frame.width 433 | let height = self.view.frame.width * size.height / size.width 434 | videoView.frame = CGRect( 435 | x: 0, 436 | y: (self.view.frame.height - height) / 2, 437 | width: width, 438 | height: height) 439 | } 440 | } 441 | 442 | 443 | 444 | 445 | 446 | 447 | extension ChatViewController { 448 | 449 | } 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | -------------------------------------------------------------------------------- /WebRTCHandsOn/CONTROLLER/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // WebRTCHandsOn 4 | // 5 | // Created by Takumi Minamoto on 2017/05/27. 6 | // Copyright © 2017 tnoho. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | let APP_DELGATE = UIApplication.shared.delegate as! AppDelegate 12 | 13 | class ViewController: UIViewController { 14 | 15 | override func viewDidLoad() { 16 | super.viewDidLoad() 17 | // Do any additional setup after loading the view, typically from a nib. 18 | } 19 | 20 | override func didReceiveMemoryWarning() { 21 | super.didReceiveMemoryWarning() 22 | // Dispose of any resources that can be recreated. 23 | } 24 | 25 | @IBAction func connectButtonAction(_ sender: Any) { 26 | self.performSegue(withIdentifier: "joinToRoom", sender: nil) 27 | // APP_DELGATE.callManager?.sendCall() 28 | } 29 | 30 | } 31 | 32 | -------------------------------------------------------------------------------- /WebRTCHandsOn/SERVICE/CallManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CallManager.swift 3 | // WebRTCHandsOn 4 | // 5 | // Created by dang nguyenhuu on 2018/02/13. 6 | // Copyright © 2018 tnoho. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | import CallKit 12 | 13 | 14 | class CallManager: NSObject { 15 | 16 | private var provider: CXProvider 17 | 18 | override init() { 19 | self.provider = CXProvider(configuration: CXProviderConfiguration(localizedName: "My App")) 20 | super.init() 21 | provider.setDelegate(self, queue: nil) 22 | 23 | } 24 | 25 | 26 | func receiveCall() { 27 | 28 | let update = CXCallUpdate() 29 | update.remoteHandle = CXHandle(type: .generic, value: "Pete Za") 30 | provider.reportNewIncomingCall(with: UUID(), update: update, completion: { error in }) 31 | 32 | } 33 | 34 | 35 | func sendCall() { 36 | 37 | let controller = CXCallController() 38 | let transaction = CXTransaction(action: CXStartCallAction(call: UUID(), handle: CXHandle(type: .generic, value: "Pete Za"))) 39 | controller.request(transaction, completion: { error in }) 40 | 41 | } 42 | 43 | 44 | } 45 | 46 | 47 | 48 | 49 | 50 | 51 | extension CallManager: CXProviderDelegate { 52 | 53 | func providerDidReset(_ provider: CXProvider) { 54 | 55 | print(#function) 56 | } 57 | 58 | 59 | func providerDidBegin(_ provider: CXProvider) { 60 | print(#function) 61 | } 62 | 63 | 64 | func provider(_ provider: CXProvider, perform action: CXStartCallAction) { 65 | print("CXStartCallAction") 66 | } 67 | 68 | 69 | func provider(_ provider: CXProvider, perform action: CXSetMutedCallAction) { 70 | print("CXSetMutedCallAction") 71 | } 72 | 73 | 74 | func provider(_ provider: CXProvider, perform action: CXSetHeldCallAction) { 75 | print("CXSetHeldCallAction") 76 | } 77 | 78 | 79 | func provider(_ provider: CXProvider, perform action: CXPlayDTMFCallAction) { 80 | print("CXPlayDTMFCallAction") 81 | } 82 | 83 | 84 | 85 | 86 | 87 | 88 | func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) { 89 | action.fulfill() 90 | print("Answer") 91 | } 92 | 93 | 94 | func provider(_ provider: CXProvider, perform action: CXEndCallAction) { 95 | action.fulfill() 96 | print("Cancel") 97 | } 98 | 99 | 100 | 101 | } 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /WebRTCHandsOn/SERVICE/WebRTC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WebRTC.swift 3 | // WebRTCHandsOn 4 | // 5 | // Created by dang nguyenhuu on 2018/02/13. 6 | // Copyright © 2018 tnoho. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | -------------------------------------------------------------------------------- /WebRTCHandsOn/Support/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | } 88 | ], 89 | "info" : { 90 | "version" : 1, 91 | "author" : "xcode" 92 | } 93 | } -------------------------------------------------------------------------------- /WebRTCHandsOn/Support/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 | -------------------------------------------------------------------------------- /WebRTCHandsOn/Support/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | UIBackgroundModes 6 | 7 | voip 8 | 9 | CFBundleDevelopmentRegion 10 | en 11 | CFBundleExecutable 12 | $(EXECUTABLE_NAME) 13 | CFBundleIdentifier 14 | $(PRODUCT_BUNDLE_IDENTIFIER) 15 | CFBundleInfoDictionaryVersion 16 | 6.0 17 | CFBundleName 18 | $(PRODUCT_NAME) 19 | CFBundlePackageType 20 | APPL 21 | CFBundleShortVersionString 22 | 1.0 23 | CFBundleVersion 24 | 1 25 | LSRequiresIPhoneOS 26 | 27 | NSCameraUsageDescription 28 | カメラを利用します 29 | NSMicrophoneUsageDescription 30 | マイクを利用します 31 | UILaunchStoryboardName 32 | LaunchScreen 33 | UIMainStoryboardFile 34 | Main 35 | UIRequiredDeviceCapabilities 36 | 37 | armv7 38 | 39 | UISupportedInterfaceOrientations 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationLandscapeLeft 43 | UIInterfaceOrientationLandscapeRight 44 | 45 | UISupportedInterfaceOrientations~ipad 46 | 47 | UIInterfaceOrientationPortrait 48 | UIInterfaceOrientationPortraitUpsideDown 49 | UIInterfaceOrientationLandscapeLeft 50 | UIInterfaceOrientationLandscapeRight 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /WebRTCHandsOn/Utility/Utility.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Utility.swift 3 | // WebRTCHandsOn 4 | // 5 | // Created by dang nguyenhuu on 2018/02/13. 6 | // Copyright © 2018 tnoho. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | 12 | 13 | // 参考にさせていただきました!Thanks: http://seesaakyoto.seesaa.net/article/403680516.html 14 | func LOG(_ body: String = "", function: String = #function, line: Int = #line) { 15 | print("[\(function) : \(line)] \(body)") 16 | } 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | --------------------------------------------------------------------------------