├── .gitignore
├── README.md
├── art
├── cordova-rtc-logo.png
├── icon-white.png
├── icon.png
├── jssip_freeswitch_1.png
├── jssip_freeswitch_2.png
└── local_1.png
├── config.xml
├── hooks
└── README.md
├── iosrtc.sh
├── package-lock.json
├── package.json
├── res
├── README.md
├── icon
│ └── ios
│ │ ├── AppIcon24x24@2x.png
│ │ ├── AppIcon27.5x27.5@2x.png
│ │ ├── AppIcon29x29@2x.png
│ │ ├── AppIcon29x29@3x.png
│ │ ├── AppIcon40x40@2x.png
│ │ ├── AppIcon44x44@2x.png
│ │ ├── AppIcon86x86@2x.png
│ │ ├── AppIcon98x98@2x.png
│ │ ├── icon-1024.png
│ │ ├── icon-20.png
│ │ ├── icon-20@2x.png
│ │ ├── icon-20@3x.png
│ │ ├── icon-40.png
│ │ ├── icon-40@2x.png
│ │ ├── icon-50.png
│ │ ├── icon-50@2x.png
│ │ ├── icon-60@2x.png
│ │ ├── icon-60@3x.png
│ │ ├── icon-72.png
│ │ ├── icon-72@2x.png
│ │ ├── icon-76.png
│ │ ├── icon-76@2x.png
│ │ ├── icon-83.5@2x.png
│ │ ├── icon-small.png
│ │ ├── icon-small@2x.png
│ │ ├── icon-small@3x.png
│ │ ├── icon.png
│ │ └── icon@2x.png
└── screen
│ └── ios
│ ├── Default-2436h.png
│ ├── Default-568h@2x~iphone.png
│ ├── Default-667h.png
│ ├── Default-736h.png
│ ├── Default-Landscape-2436h.png
│ ├── Default-Landscape-736h.png
│ ├── Default-Landscape@2x~ipad.png
│ ├── Default-Landscape~ipad.png
│ ├── Default-Portrait@2x~ipad.png
│ ├── Default-Portrait~ipad.png
│ ├── Default@2x~iphone.png
│ └── Default~iphone.png
└── www
├── assets
└── Sunset.mp4
├── css
└── index.css
├── img
└── logo.png
├── index.html
├── js
├── common.js
├── index-easyrtc.js
├── index-janus.js
├── index-jssip.js
├── index-local.js
├── index-twilio.js
└── index-websocket.js
└── lib
└── ios-websocket-hack.js
/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules/
2 | /TODO
3 | /NO_GIT/
4 | .DS_Store
5 | /plugins/
6 | /platforms/
7 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # cordova-plugin-iosrtc-sample
4 |
5 | Basic sample application using cordova-plugin-iosrtc.
6 |
7 | 
8 |
9 | ## Requirements
10 |
11 | In order to make this Cordova plugin run into a iOS application some requirements must be satisfied in both development computer and target devices:
12 |
13 | * Xcode >= 11.1 (11A1027)
14 | * iOS >= 10.2 (run on lower versions at your own risk, don't report issues)
15 | * `swift-version` => 4.2
16 | * `cordova` >= 7.1.0
17 | * `cordova-ios` >= 4.5.1
18 |
19 | ### Third-Party Library Examples
20 |
21 | * WebRTC W3C v1.0.0
22 | * WebRTC.framework => M69
23 | * WebSocket
24 | * Janus => 0.7.4
25 | * JSSip => 3.1.2
26 | * Sip.js => 0.15.6
27 | * Twilio => 2.0.0
28 | * Open-Easyrtc => 2.0.5
29 |
30 | ## Author
31 |
32 | [Harold Thetiot](https://sylaps.com)
33 |
34 |
35 | ### Maintainers
36 |
37 | * [Harold Thetiot](https://sylaps.com)
38 |
39 | ## License
40 |
41 | [MIT](./LICENSE) :)
42 |
--------------------------------------------------------------------------------
/art/cordova-rtc-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cordova-rtc/cordova-plugin-iosrtc-sample/6b75a940059e8c83c885cd6a52e456338a009dda/art/cordova-rtc-logo.png
--------------------------------------------------------------------------------
/art/icon-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cordova-rtc/cordova-plugin-iosrtc-sample/6b75a940059e8c83c885cd6a52e456338a009dda/art/icon-white.png
--------------------------------------------------------------------------------
/art/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cordova-rtc/cordova-plugin-iosrtc-sample/6b75a940059e8c83c885cd6a52e456338a009dda/art/icon.png
--------------------------------------------------------------------------------
/art/jssip_freeswitch_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cordova-rtc/cordova-plugin-iosrtc-sample/6b75a940059e8c83c885cd6a52e456338a009dda/art/jssip_freeswitch_1.png
--------------------------------------------------------------------------------
/art/jssip_freeswitch_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cordova-rtc/cordova-plugin-iosrtc-sample/6b75a940059e8c83c885cd6a52e456338a009dda/art/jssip_freeswitch_2.png
--------------------------------------------------------------------------------
/art/local_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cordova-rtc/cordova-plugin-iosrtc-sample/6b75a940059e8c83c885cd6a52e456338a009dda/art/local_1.png
--------------------------------------------------------------------------------
/config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Cordova iOSRTC Sample
4 |
5 | A sample Apache Cordova iOSRTC application that use cordova-plugin-iosrtc
6 |
7 |
8 | Cordova-RTC Team
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
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 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 | This application requires access to the devices camera to record and send the users camera image via video chat functionality
95 |
96 |
97 | TThis application requires access to the devices image library to display an image as the users profile picture and/or send images via message chat functionality
98 |
99 |
100 | This application requires access to the devices microphone to record and send the users voice message via video/voice chat functionality
101 |
102 |
103 | This application requires access to the bluetooth wireless headphones and microphone to broadcast or record and send the users voice message via video/voice chat functionality
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
--------------------------------------------------------------------------------
/hooks/README.md:
--------------------------------------------------------------------------------
1 |
21 | # Cordova Hooks
22 |
23 | Cordova Hooks represent special scripts which could be added by application and plugin developers or even by your own build system to customize cordova commands. See Hooks Guide for more details: http://cordova.apache.org/docs/en/edge/guide_appdev_hooks_index.md.html#Hooks%20Guide.
24 |
--------------------------------------------------------------------------------
/iosrtc.sh:
--------------------------------------------------------------------------------
1 |
2 | # Change test version for iOSRTCApp
3 | cordova plugin remove cordova-plugin-iosrtc --verbose
4 | #cordova plugin add file://./../cordova-plugin-iosrtc --verbose
5 | cordova plugin add https://github.com/cordova-rtc/cordova-plugin-iosrtc#master --verbose
6 | #cordova plugin add https://github.com/cordova-rtc/cordova-plugin-iosrtc#bugs/RTCDispatcherTypeMainGeneratingDeviceOrientationNotifications --verbose
7 | #cordova plugin add https://github.com/cordova-rtc/cordova-plugin-iosrtc#6.0.13-RC2 --verbose
8 | #cordova plugin add https://github.com/cordova-rtc/cordova-plugin-iosrtc#bugs/ontrack --verbose
9 | #cordova plugin add https://github.com/cordova-rtc/cordova-plugin-iosrtc#bugs/getStats --verbose
10 | #cordova plugin add https://github.com/cordova-rtc/cordova-plugin-iosrtc#bugs/Blob --verbose
11 | #cordova platform remove ios --no-save
12 | #cordova platform add ios@latest --no-save
13 | cordova prepare ios
14 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "cordova-plugin-iosrtc-sample",
3 | "displayName": "cordovaPluginIosrtcSample",
4 | "version": "1.0.0",
5 | "description": "A sample Apache Cordova application that responds to the deviceready event.",
6 | "main": "index.js",
7 | "scripts": {
8 | "test": "echo \"Error: no test specified\" && exit 1"
9 | },
10 | "author": "Apache Cordova Team",
11 | "license": "Apache-2.0",
12 | "cordova": {
13 | "plugins": {
14 | "cordova-plugin-whitelist": {},
15 | "cordova.plugins.diagnostic": {},
16 | "cordova-plugin-statusbar": {},
17 | "cordova-plugin-background-mode": {},
18 | "cordova-plugin-ipad-multitasking": {},
19 | "cordova-custom-config": {},
20 | "cordova-plugin-iosrtc": {
21 | "MANUAL_INIT_AUDIO_DEVICE": "FALSE"
22 | }
23 | },
24 | "platforms": [
25 | "android",
26 | "browser",
27 | "ios"
28 | ]
29 | },
30 | "devDependencies": {
31 | "cordova": "^10.0.0",
32 | "cordova-android": "^8.1.0",
33 | "cordova-browser": "^6.0.0",
34 | "cordova-custom-config": "^5.1.0",
35 | "cordova-plugin-background-mode": "^0.7.3",
36 | "cordova-plugin-device": "^2.0.3",
37 | "cordova-plugin-ipad-multitasking": "^0.1.1",
38 | "cordova-plugin-statusbar": "^2.4.3",
39 | "cordova-plugin-whitelist": "^1.3.4",
40 | "cordova.plugins.diagnostic": "^7.1.1",
41 | "cordova-ios": "^6.1.1",
42 | "cordova-plugin-iosrtc": "git+https://github.com/cordova-rtc/cordova-plugin-iosrtc.git#master",
43 | "plugin": "^0.3.3"
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/res/README.md:
--------------------------------------------------------------------------------
1 |
21 |
22 | Note that these image resources are not copied into a project when a project
23 | is created with the CLI. Although there are default image resources in a
24 | newly-created project, those come from the platform-specific project template,
25 | which can generally be found in the platform's `template` directory. Until
26 | icon and splashscreen support is added to the CLI, these image resources
27 | aren't used directly.
28 |
29 | See https://issues.apache.org/jira/browse/CB-5145
30 |
--------------------------------------------------------------------------------
/res/icon/ios/AppIcon24x24@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cordova-rtc/cordova-plugin-iosrtc-sample/6b75a940059e8c83c885cd6a52e456338a009dda/res/icon/ios/AppIcon24x24@2x.png
--------------------------------------------------------------------------------
/res/icon/ios/AppIcon27.5x27.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cordova-rtc/cordova-plugin-iosrtc-sample/6b75a940059e8c83c885cd6a52e456338a009dda/res/icon/ios/AppIcon27.5x27.5@2x.png
--------------------------------------------------------------------------------
/res/icon/ios/AppIcon29x29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cordova-rtc/cordova-plugin-iosrtc-sample/6b75a940059e8c83c885cd6a52e456338a009dda/res/icon/ios/AppIcon29x29@2x.png
--------------------------------------------------------------------------------
/res/icon/ios/AppIcon29x29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cordova-rtc/cordova-plugin-iosrtc-sample/6b75a940059e8c83c885cd6a52e456338a009dda/res/icon/ios/AppIcon29x29@3x.png
--------------------------------------------------------------------------------
/res/icon/ios/AppIcon40x40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cordova-rtc/cordova-plugin-iosrtc-sample/6b75a940059e8c83c885cd6a52e456338a009dda/res/icon/ios/AppIcon40x40@2x.png
--------------------------------------------------------------------------------
/res/icon/ios/AppIcon44x44@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cordova-rtc/cordova-plugin-iosrtc-sample/6b75a940059e8c83c885cd6a52e456338a009dda/res/icon/ios/AppIcon44x44@2x.png
--------------------------------------------------------------------------------
/res/icon/ios/AppIcon86x86@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cordova-rtc/cordova-plugin-iosrtc-sample/6b75a940059e8c83c885cd6a52e456338a009dda/res/icon/ios/AppIcon86x86@2x.png
--------------------------------------------------------------------------------
/res/icon/ios/AppIcon98x98@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cordova-rtc/cordova-plugin-iosrtc-sample/6b75a940059e8c83c885cd6a52e456338a009dda/res/icon/ios/AppIcon98x98@2x.png
--------------------------------------------------------------------------------
/res/icon/ios/icon-1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cordova-rtc/cordova-plugin-iosrtc-sample/6b75a940059e8c83c885cd6a52e456338a009dda/res/icon/ios/icon-1024.png
--------------------------------------------------------------------------------
/res/icon/ios/icon-20.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cordova-rtc/cordova-plugin-iosrtc-sample/6b75a940059e8c83c885cd6a52e456338a009dda/res/icon/ios/icon-20.png
--------------------------------------------------------------------------------
/res/icon/ios/icon-20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cordova-rtc/cordova-plugin-iosrtc-sample/6b75a940059e8c83c885cd6a52e456338a009dda/res/icon/ios/icon-20@2x.png
--------------------------------------------------------------------------------
/res/icon/ios/icon-20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cordova-rtc/cordova-plugin-iosrtc-sample/6b75a940059e8c83c885cd6a52e456338a009dda/res/icon/ios/icon-20@3x.png
--------------------------------------------------------------------------------
/res/icon/ios/icon-40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cordova-rtc/cordova-plugin-iosrtc-sample/6b75a940059e8c83c885cd6a52e456338a009dda/res/icon/ios/icon-40.png
--------------------------------------------------------------------------------
/res/icon/ios/icon-40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cordova-rtc/cordova-plugin-iosrtc-sample/6b75a940059e8c83c885cd6a52e456338a009dda/res/icon/ios/icon-40@2x.png
--------------------------------------------------------------------------------
/res/icon/ios/icon-50.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cordova-rtc/cordova-plugin-iosrtc-sample/6b75a940059e8c83c885cd6a52e456338a009dda/res/icon/ios/icon-50.png
--------------------------------------------------------------------------------
/res/icon/ios/icon-50@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cordova-rtc/cordova-plugin-iosrtc-sample/6b75a940059e8c83c885cd6a52e456338a009dda/res/icon/ios/icon-50@2x.png
--------------------------------------------------------------------------------
/res/icon/ios/icon-60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cordova-rtc/cordova-plugin-iosrtc-sample/6b75a940059e8c83c885cd6a52e456338a009dda/res/icon/ios/icon-60@2x.png
--------------------------------------------------------------------------------
/res/icon/ios/icon-60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cordova-rtc/cordova-plugin-iosrtc-sample/6b75a940059e8c83c885cd6a52e456338a009dda/res/icon/ios/icon-60@3x.png
--------------------------------------------------------------------------------
/res/icon/ios/icon-72.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cordova-rtc/cordova-plugin-iosrtc-sample/6b75a940059e8c83c885cd6a52e456338a009dda/res/icon/ios/icon-72.png
--------------------------------------------------------------------------------
/res/icon/ios/icon-72@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cordova-rtc/cordova-plugin-iosrtc-sample/6b75a940059e8c83c885cd6a52e456338a009dda/res/icon/ios/icon-72@2x.png
--------------------------------------------------------------------------------
/res/icon/ios/icon-76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cordova-rtc/cordova-plugin-iosrtc-sample/6b75a940059e8c83c885cd6a52e456338a009dda/res/icon/ios/icon-76.png
--------------------------------------------------------------------------------
/res/icon/ios/icon-76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cordova-rtc/cordova-plugin-iosrtc-sample/6b75a940059e8c83c885cd6a52e456338a009dda/res/icon/ios/icon-76@2x.png
--------------------------------------------------------------------------------
/res/icon/ios/icon-83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cordova-rtc/cordova-plugin-iosrtc-sample/6b75a940059e8c83c885cd6a52e456338a009dda/res/icon/ios/icon-83.5@2x.png
--------------------------------------------------------------------------------
/res/icon/ios/icon-small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cordova-rtc/cordova-plugin-iosrtc-sample/6b75a940059e8c83c885cd6a52e456338a009dda/res/icon/ios/icon-small.png
--------------------------------------------------------------------------------
/res/icon/ios/icon-small@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cordova-rtc/cordova-plugin-iosrtc-sample/6b75a940059e8c83c885cd6a52e456338a009dda/res/icon/ios/icon-small@2x.png
--------------------------------------------------------------------------------
/res/icon/ios/icon-small@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cordova-rtc/cordova-plugin-iosrtc-sample/6b75a940059e8c83c885cd6a52e456338a009dda/res/icon/ios/icon-small@3x.png
--------------------------------------------------------------------------------
/res/icon/ios/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cordova-rtc/cordova-plugin-iosrtc-sample/6b75a940059e8c83c885cd6a52e456338a009dda/res/icon/ios/icon.png
--------------------------------------------------------------------------------
/res/icon/ios/icon@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cordova-rtc/cordova-plugin-iosrtc-sample/6b75a940059e8c83c885cd6a52e456338a009dda/res/icon/ios/icon@2x.png
--------------------------------------------------------------------------------
/res/screen/ios/Default-2436h.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cordova-rtc/cordova-plugin-iosrtc-sample/6b75a940059e8c83c885cd6a52e456338a009dda/res/screen/ios/Default-2436h.png
--------------------------------------------------------------------------------
/res/screen/ios/Default-568h@2x~iphone.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cordova-rtc/cordova-plugin-iosrtc-sample/6b75a940059e8c83c885cd6a52e456338a009dda/res/screen/ios/Default-568h@2x~iphone.png
--------------------------------------------------------------------------------
/res/screen/ios/Default-667h.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cordova-rtc/cordova-plugin-iosrtc-sample/6b75a940059e8c83c885cd6a52e456338a009dda/res/screen/ios/Default-667h.png
--------------------------------------------------------------------------------
/res/screen/ios/Default-736h.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cordova-rtc/cordova-plugin-iosrtc-sample/6b75a940059e8c83c885cd6a52e456338a009dda/res/screen/ios/Default-736h.png
--------------------------------------------------------------------------------
/res/screen/ios/Default-Landscape-2436h.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cordova-rtc/cordova-plugin-iosrtc-sample/6b75a940059e8c83c885cd6a52e456338a009dda/res/screen/ios/Default-Landscape-2436h.png
--------------------------------------------------------------------------------
/res/screen/ios/Default-Landscape-736h.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cordova-rtc/cordova-plugin-iosrtc-sample/6b75a940059e8c83c885cd6a52e456338a009dda/res/screen/ios/Default-Landscape-736h.png
--------------------------------------------------------------------------------
/res/screen/ios/Default-Landscape@2x~ipad.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cordova-rtc/cordova-plugin-iosrtc-sample/6b75a940059e8c83c885cd6a52e456338a009dda/res/screen/ios/Default-Landscape@2x~ipad.png
--------------------------------------------------------------------------------
/res/screen/ios/Default-Landscape~ipad.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cordova-rtc/cordova-plugin-iosrtc-sample/6b75a940059e8c83c885cd6a52e456338a009dda/res/screen/ios/Default-Landscape~ipad.png
--------------------------------------------------------------------------------
/res/screen/ios/Default-Portrait@2x~ipad.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cordova-rtc/cordova-plugin-iosrtc-sample/6b75a940059e8c83c885cd6a52e456338a009dda/res/screen/ios/Default-Portrait@2x~ipad.png
--------------------------------------------------------------------------------
/res/screen/ios/Default-Portrait~ipad.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cordova-rtc/cordova-plugin-iosrtc-sample/6b75a940059e8c83c885cd6a52e456338a009dda/res/screen/ios/Default-Portrait~ipad.png
--------------------------------------------------------------------------------
/res/screen/ios/Default@2x~iphone.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cordova-rtc/cordova-plugin-iosrtc-sample/6b75a940059e8c83c885cd6a52e456338a009dda/res/screen/ios/Default@2x~iphone.png
--------------------------------------------------------------------------------
/res/screen/ios/Default~iphone.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cordova-rtc/cordova-plugin-iosrtc-sample/6b75a940059e8c83c885cd6a52e456338a009dda/res/screen/ios/Default~iphone.png
--------------------------------------------------------------------------------
/www/assets/Sunset.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cordova-rtc/cordova-plugin-iosrtc-sample/6b75a940059e8c83c885cd6a52e456338a009dda/www/assets/Sunset.mp4
--------------------------------------------------------------------------------
/www/css/index.css:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the Apache Software Foundation (ASF) under one
3 | * or more contributor license agreements. See the NOTICE file
4 | * distributed with this work for additional information
5 | * regarding copyright ownership. The ASF licenses this file
6 | * to you under the Apache License, Version 2.0 (the
7 | * "License"); you may not use this file except in compliance
8 | * with the License. You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing,
13 | * software distributed under the License is distributed on an
14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 | * KIND, either express or implied. See the License for the
16 | * specific language governing permissions and limitations
17 | * under the License.
18 | */
19 | * {
20 | -webkit-tap-highlight-color: rgba(0,0,0,0); /* make transparent link selection, adjust last value opacity 0 to 1.0 */
21 | }
22 |
23 | html {
24 | height:100%;
25 | width:100%;
26 | -webkit-text-size-adjust: none;
27 | touch-action: manipulation;
28 | }
29 |
30 | body {
31 | -webkit-touch-callout: none; /* prevent callout to copy image, etc when tap to hold */
32 | -webkit-text-size-adjust: none; /* prevent webkit from resizing text to fit */
33 | -webkit-user-select: none; /* prevent copy paste, to allow, change 'none' to 'text' */
34 | font-family: "RobotoDraft", "Roboto", "Helvetica", sans-serif;
35 | font-size:12px;
36 | height:100%;
37 | width:100%;
38 | margin:0px;
39 | padding:0px;
40 | }
41 |
42 | /* Portrait layout (default) */
43 | .app {
44 | display: flex;
45 | position: relative;
46 | height: 100%;
47 | width: 100%;
48 | background-color: #333;
49 | color: #fff;
50 | }
51 |
52 | .hidden {
53 | display: none !important;
54 | }
55 |
56 | video {
57 | display: flex;
58 | width: 100%;
59 | height: 100%;
60 | max-width: 100%;
61 | max-width: 100%;
62 | }
63 |
64 | .remote-stream {
65 | position: absolute;
66 | display: flex;
67 | align-items: center;
68 | justify-content: center;
69 | flex: 1 1 100%;
70 | width: 100%;
71 | height: 100%;
72 | max-height: 100%;
73 | }
74 |
75 | .remote-stream video {
76 | object-fit: cover;
77 | }
78 |
79 | .remote-stream video.video-only {
80 | object-fit: contain;
81 | }
82 |
83 | .local-stream {
84 | position: absolute;
85 | display: flex;
86 | align-items: center;
87 | justify-content: center;
88 | box-sizing: border-box;
89 | right: 0;
90 | padding: 10px 10px 0 0;
91 | width: 33%;
92 | height: 25%;
93 | }
94 |
95 | .local-stream video {
96 | object-fit: cover;
97 | border-radius: 10px;
98 | z-index: 10;
99 | }
100 |
101 | .local-stream video {
102 | -webkit-transform: scaleX(-1);
103 | -moz-transform: scaleX(-1);
104 | -ms-transform: scaleX(-1);
105 | -o-transform: scaleX(-1);
106 | transform: scaleX(-1);
107 | }
108 |
109 | /* Button */
110 |
111 | .btn {
112 | border: none;
113 | background: transparent;
114 | }
115 |
116 | .btn.btn-round {
117 | box-shadow: 0 3px 5px -1px rgba(0,0,0,.2), 0 6px 10px 0 rgba(0,0,0,.14), 0 1px 18px 0 rgba(0,0,0,.12);
118 | box-sizing: border-box;
119 | position: relative;
120 | -webkit-user-select: none;
121 | -moz-user-select: none;
122 | -ms-user-select: none;
123 | user-select: none;
124 | cursor: pointer;
125 | border: none;
126 | -webkit-tap-highlight-color: transparent;
127 | display: inline-block;
128 | white-space: nowrap;
129 | text-decoration: none;
130 | vertical-align: baseline;
131 | text-align: center;
132 | margin: 0;
133 | min-width: 64px;
134 | line-height: 36px;
135 | padding: 0 16px;
136 | border-radius: 4px;
137 | overflow: visible;
138 | transition: background .4s cubic-bezier(.25,.8,.25,1),box-shadow 280ms cubic-bezier(.4,0,.2,1);
139 | min-width: 0;
140 | border-radius: 50%;
141 | width: 48px;
142 | height: 48px;
143 | padding: 0;
144 | flex-shrink: 0;
145 | background-color: rgba(149, 151, 150, 0.53);
146 | line-height: 0px;
147 | }
148 |
149 | .btn.btn-active{
150 | background-color: #01579b;
151 | }
152 |
153 |
154 | .btn.btn-danger {
155 | background-color: #f44336;
156 | }
157 |
158 | .btn.btn-call {
159 | background-color: #4caf50;
160 | width: 56px;
161 | height: 56px;
162 | }
163 |
164 | .btn.btn-settings {
165 | background-color: #f44336;
166 | width: 84px;
167 | height: 84px;
168 | }
169 |
170 | .btn.btn-hangup {
171 | width: 56px;
172 | height: 56px;
173 | }
174 |
175 | .btn.btn-report {
176 | width: 56px;
177 | height: 56px;
178 | background-color: #ffb300;
179 | box-shadow: 0 3px 1px -2px rgba(0,0,0,.2), 0 2px 2px 0 rgba(0,0,0,.14), 0 1px 5px 0 rgba(0,0,0,.12);
180 | }
181 |
182 | .btn i {
183 | color: #fff;
184 | }
185 |
186 | /* */
187 |
188 | .notice {
189 | z-index: 200;
190 | padding: 10px;
191 | box-sizing: border-box;
192 | background-color: rgba(51, 51, 51, .5);
193 | border-radius: 10px;
194 | position: absolute;
195 | }
196 |
197 | /* Controls */
198 |
199 | .controls {
200 | z-index: 100;
201 | padding: 10px;
202 | box-sizing: border-box;
203 | }
204 |
205 | .top-controls {
206 | position: absolute;
207 | display: flex;
208 | flex-direction: column;
209 | top: 0;
210 | }
211 |
212 | .top-controls .btn{
213 | margin: 10px 0;
214 | }
215 |
216 | .footer-controls {
217 | position: absolute;
218 | bottom: 0;
219 | width: 100%;
220 | display: flex;
221 | align-items: center;
222 | justify-content: center;
223 | }
224 |
225 | .footer-controls .btn{
226 | margin: 0 10px;
227 | }
228 |
229 | .local-controls {
230 | position: absolute;
231 | top: 26%;
232 | width: 33%;
233 | right: 0;
234 | display: flex;
235 | align-items: center;
236 | justify-content: center;
237 | }
238 |
239 | .report-controls {
240 | position: absolute;
241 | }
242 |
243 | .report-controls {
244 | position: absolute;
245 | top: 65vh;
246 | right: 0;
247 | padding-right: 0;
248 | }
249 |
250 | /* Landscape layout (with min-width) */
251 | @media screen and (min-aspect-ratio: 1/1) and (min-width:400px) {
252 | .report-controls {
253 | top: 75vh;
254 | }
255 |
256 | .local-stream {
257 | width: 25%;
258 | height: 33%;
259 | }
260 |
261 | .local-controls {
262 | top: 34%;
263 | width: 25%;
264 | }
265 | }
266 |
267 | .material-icons {
268 | font-family: 'Material Icons';
269 | font-weight: normal;
270 | font-style: normal;
271 | font-size: 24px; /* Preferred icon size */
272 | display: inline-block;
273 | line-height: 1;
274 | text-transform: none;
275 | letter-spacing: normal;
276 | word-wrap: normal;
277 | white-space: nowrap;
278 | direction: ltr;
279 |
280 | /* Support for all WebKit browsers. */
281 | -webkit-font-smoothing: antialiased;
282 | /* Support for Safari and Chrome. */
283 | text-rendering: optimizeLegibility;
284 |
285 | /* Support for Firefox. */
286 | -moz-osx-font-smoothing: grayscale;
287 |
288 | /* Support for IE. */
289 | font-feature-settings: 'liga';
290 | }
291 |
292 | .material-icons {
293 | color: white;
294 | }
295 |
--------------------------------------------------------------------------------
/www/img/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cordova-rtc/cordova-plugin-iosrtc-sample/6b75a940059e8c83c885cd6a52e456338a009dda/www/img/logo.png
--------------------------------------------------------------------------------
/www/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | Cordova-RTC
14 |
15 |
16 |
17 |
18 |
19 |
Loading...
20 |
21 |
22 | Press Call button to start the call.
23 |
24 |
25 |
45 |
46 |
47 |
48 |
49 |
52 |
53 |
54 |
57 |
60 |
63 |
66 |
69 |
70 |
71 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
--------------------------------------------------------------------------------
/www/js/common.js:
--------------------------------------------------------------------------------
1 | // Config
2 | // Note: Change to match your adapter version
3 |
4 | var adapterVersion = 'latest';
5 | var adapterUrl = "https://webrtc.github.io/adapter/adapter-" + adapterVersion + ".js";
6 |
7 | // Detect if current enviroment is cordova
8 | var isCordova = !document.URL.includes('http');
9 |
10 | //
11 | // Container
12 | //
13 |
14 | var appContainer = document.querySelector('.app');
15 |
16 | //
17 | // Utils
18 | //
19 |
20 | function uuid4() {
21 | function hex(s, b) {
22 | return s +
23 | (b >>> 4).toString(16) + // high nibble
24 | (b & 0b1111).toString(16); // low nibble
25 | }
26 |
27 | var r = crypto.getRandomValues(new Uint8Array(16));
28 |
29 | r[6] = r[6] >>> 4 | 0b01000000; // Set type 4: 0100
30 | r[8] = r[8] >>> 3 | 0b10000000; // Set variant: 100
31 |
32 | return r.slice(0, 4).reduce(hex, '') +
33 | r.slice(4, 6).reduce(hex, '-') +
34 | r.slice(6, 8).reduce(hex, '-') +
35 | r.slice(8, 10).reduce(hex, '-') +
36 | r.slice(10, 16).reduce(hex, '-');
37 | }
38 |
39 | function loadScript(scriptUrl) {
40 | return new Promise(function(resolve, reject) {
41 | // load adapter.js
42 | var script = document.createElement("script");
43 | script.type = "text/javascript";
44 | script.src = scriptUrl;
45 | script.async = false;
46 | document.getElementsByTagName("head")[0].appendChild(script);
47 | script.onload = function() {
48 | console.debug('loadScript.loaded', script.src);
49 | resolve();
50 | };
51 | });
52 | }
53 |
54 | //
55 | // Event debug
56 | //
57 |
58 | var excludeEvents = [
59 | 'ontimeupdate', 'onprogress',
60 | 'onmousemove', 'onmouseover', 'onmouseout', 'onmouseenter', 'onmouseleave',
61 | 'onpointermove', 'onpointerover', 'onpointerenter', 'onpointerrawupdate', 'onpointerout', 'onpointerleave',
62 | 'onpointerdown', 'onmousedown', 'onclick', 'onmouseup', 'onpointerup'
63 | ];
64 |
65 | function TestListenEvents(eventTarget) {
66 |
67 | if (eventTarget.TestListenEvents) {
68 | return;
69 | }
70 | eventTarget.TestListenEvents = true;
71 |
72 | function eventTriggered(event) {
73 | console.debug('event.triggered', event, eventTarget);
74 | }
75 |
76 | for(var eventName in eventTarget) {
77 | if (eventName.search('on') === 0 && excludeEvents.indexOf(eventName) == -1) {
78 | eventTarget.addEventListener(eventName.slice(2), eventTriggered.bind(eventTarget, eventName));
79 | }
80 | }
81 | }
82 |
83 | //
84 | // MediaStreams
85 | //
86 |
87 | var localStream, localVideoEl, localDeviceId;
88 | function TestGetUserMedia(deviceId) {
89 |
90 | if (!deviceId) {
91 | return navigator.mediaDevices.enumerateDevices().then(function(devices) {
92 | var newDevice = devices.filter(function(device) {
93 | return device.kind === 'videoinput';
94 | }).find(function(device, idx) {
95 | return device.deviceId !== 'default';
96 | });
97 |
98 | localDeviceId = newDevice ? newDevice.deviceId : null;
99 | return TestGetUserMedia(localDeviceId || 'default');
100 | });
101 | }
102 |
103 | console.debug('TestGetUserMedia', deviceId);
104 | return navigator.mediaDevices.getUserMedia({
105 | /*
106 | video: true,
107 | */
108 | video: {
109 | deviceId: deviceId,
110 |
111 | /*
112 | width: {
113 | max: 1280,
114 | min: 640
115 | },
116 | height: {
117 | max: 720,
118 | min: 480
119 | },
120 | */
121 | /*
122 | height: {
123 | ideal: 240,
124 | min: 180,
125 | max: 480
126 | },
127 | */
128 | //facingMode: 'environment'
129 | //height: 480,
130 | //width: 1280,
131 | //height: 720,
132 | //width: 1280,
133 | //height: 960,
134 | //aspectRatio: 16/9,
135 | //aspectRatio: 11/9,
136 | //aspectRatio: 4/3,
137 | //frameRate:{ min: 30.0, max: 30.0 }
138 | },
139 | audio: true
140 | /*
141 | video: {
142 | // Test Back Camera
143 | //deviceId: 'com.apple.avfoundation.avcapturedevice.built-in_video:0'
144 | //sourceId: 'com.apple.avfoundation.avcapturedevice.built-in_video:0'
145 | deviceId: {
146 | exact: 'com.apple.avfoundation.avcapturedevice.built-in_video:0'
147 | }
148 | },
149 | audio: {
150 | deviceId: {
151 | exact: 'Built-In Microphone'
152 | }
153 | }*/
154 | }).then(function(stream) {
155 |
156 | console.debug('getUserMedia.stream', stream);
157 | console.debug('getUserMedia.stream.getTracks', stream.getTracks());
158 |
159 | TestSetLocalStream(stream);
160 |
161 | // Test mute at Start
162 | /*
163 | localStream.getAudioTracks().forEach(function (track) {
164 | track.enabled = false;
165 | });
166 | */
167 | return localStream;
168 |
169 | }).catch(function(err) {
170 | console.error('getUserMediaError', err, err.stack);
171 | });
172 | }
173 |
174 | function TestGetDisplayMedia() {
175 | console.debug('getDisplayMedia');
176 | return navigator.mediaDevices.getDisplayMedia({}).then(function(stream) {
177 |
178 | console.debug('getDisplayMedia.stream', stream);
179 | console.debug('getDisplayMedia.stream.getTracks', stream.getTracks());
180 |
181 | TestSetLocalStream(stream);
182 |
183 | // Test mute at Start
184 | /*
185 | localStream.getAudioTracks().forEach(function (track) {
186 | track.enabled = false;
187 | });
188 | */
189 |
190 | return localStream;
191 |
192 | }).catch(function(err) {
193 | console.error('getDisplayMedia', err, err.stack);
194 | });
195 | }
196 |
197 | function TestSetLocalStream(localStreamMedia) {
198 |
199 | localVideoEl = appContainer.querySelector('.local-video');
200 |
201 | // Note: Expose for debug
202 | localStream = localStreamMedia;
203 |
204 | // Listen to all events
205 | TestListenEvents(localStream);
206 | TestListenEvents(localVideoEl);
207 |
208 | // Attach local stream to video element
209 | localVideoEl.srcObject = localStream;
210 | }
211 |
212 | var peerVideoEl, peerVideoElLoader, peerStream;
213 |
214 | function TestSetPeerStreamLoading(loaded) {
215 | console.debug('TestSetPeerStreamLoading', loaded);
216 |
217 | peerVideoEl = appContainer.querySelector('.remote-video');
218 | peerVideoElLoader = appContainer.querySelector('.remote-video-loader');
219 |
220 | if (!loaded) {
221 | peerVideoElLoader.classList.remove('hidden');
222 | peerVideoEl.classList.add('hidden');
223 | } else {
224 | peerVideoElLoader.classList.add('hidden');
225 | peerVideoEl.classList.remove('hidden');
226 |
227 | if (isCordova) {
228 | appContainer.style.background = "transparent";
229 | }
230 | }
231 | }
232 |
233 | function TestSetPeerTracks(peerConnection) {
234 | var peerStreamMedia = new MediaStream();
235 | peerConnection.getReceivers().forEach(function (receiver) {
236 | console.debug('peerStreamMedia.addTrack', receiver, peerStreamMedia);
237 | peerStreamMedia.addTrack(receiver.track);
238 | });
239 |
240 | TestSetPeerStream(peerStreamMedia);
241 | }
242 |
243 | function TestSetPeerStream(peerStreamMedia) {
244 |
245 | peerVideoEl = appContainer.querySelector('.remote-video');
246 |
247 | TestSetPeerStreamLoading(true);
248 | peerVideoEl.removeEventListener('canplay', TestSetPeerStreamLoading);
249 | peerVideoEl.addEventListener('canplay', TestSetPeerStreamLoading);
250 |
251 | // Note: Expose for debug
252 | peerStream = peerStreamMedia;
253 |
254 | // Listen to all events
255 | TestListenEvents(peerStream);
256 | TestListenEvents(peerVideoEl);
257 |
258 | // Attach peer stream to video element
259 | peerVideoEl.srcObject = peerStream;
260 |
261 | // Display
262 | peerVideoEl.classList.remove('hidden');
263 |
264 | function isVideoOnly() {
265 | // Handle video only display
266 | if (peerStreamMedia.getAudioTracks().length) {
267 | peerVideoEl.classList.remove('video-only');
268 | } else {
269 | peerVideoEl.classList.add('video-only');
270 | }
271 | };
272 |
273 | isVideoOnly();
274 |
275 | peerStreamMedia.addEventListener('addtrack', isVideoOnly);
276 | peerStreamMedia.addEventListener('removetrack', isVideoOnly);
277 |
278 | if (isCordova) {
279 | appContainer.style.background = "transparent";
280 | }
281 | }
282 |
283 | function TestStopLocalMediaStream(localStream) {
284 |
285 | // Stop previous stream tracks
286 | if (localStream) {
287 | localStream.getTracks().forEach(function(track) {
288 | track.stop();
289 | });
290 |
291 | if (localVideoEl.srcObject === localStream) {
292 | localVideoEl.srcObject = null;
293 | }
294 | }
295 | }
296 |
297 | function TestSwitchCamera() {
298 |
299 | if (localStream) {
300 | TestStopLocalMediaStream(localStream);
301 | }
302 |
303 | return navigator.mediaDevices.enumerateDevices().then(function(devices) {
304 | var idx = 0;
305 | var newDevice = devices.filter(function(device) {
306 | return device.kind === 'videoinput';
307 | }).find(function(device, idx) {
308 | return device.deviceId !== localDeviceId;
309 | });
310 |
311 |
312 | localDeviceId = newDevice ? newDevice.deviceId : null;
313 |
314 | console.debug('TestSwitchCamera', localDeviceId);
315 |
316 | if (localDeviceId) {
317 | return TestGetUserMedia(localDeviceId);
318 | } else if (typeof navigator.mediaDevices.getDisplayMedia !== 'undefined') {
319 | return TestGetDisplayMedia();
320 | }
321 |
322 | }, function(err) {
323 | console.error('enumerateDevices.err', err);
324 | });
325 | }
326 |
327 | function TestAddStreamToPeerConnection(peerConnection, localStream) {
328 | try {
329 | if (!isCordova && typeof peerConnection.addStream === 'function') {
330 | peerConnection.addStream(localStream);
331 | } else {
332 | var localPeerStream = new MediaStream();
333 | localStream.getTracks().forEach(function(track) {
334 | console.debug('peerConnection.addTrack', peerConnection, track);
335 | peerConnection.addTrack(track, localPeerStream);
336 | });
337 | }
338 | } catch (err) {
339 | console.error('TestAddStreamToPeerConnection.err', err);
340 | }
341 | }
342 |
343 | function TestRemoveStreamToPeerConnection(peerConnection, localStream) {
344 | try {
345 | if (typeof peerConnection.removeStream === 'function') {
346 | peerConnection.removeStream(localStream);
347 | } else {
348 | peerConnection.getSenders().forEach(function(track) {
349 | console.debug('peerConnection.removeTrack', peerConnection, track);
350 | peerConnection.removeTrack(track);
351 | });
352 | }
353 | } catch (err) {
354 | console.error('TestRemoveStreamToPeerConnection.err', err);
355 | }
356 | }
357 |
358 | //
359 | // PeerConnections
360 | //
361 |
362 | var peerConnections = {};
363 |
364 | function TestHangupRTCPeerConnection(targetId, peerConnection) {
365 | peerConnection = peerConnection || peerConnections[targetId];
366 | console.debug('TestHangupRTCPeerConnection', targetId, peerConnection);
367 | delete peerConnections[targetId];
368 | delete peerConnectionsCandicates[targetId];
369 | peerConnection.close();
370 |
371 | if (Object.keys(peerConnections).length === 0) {
372 | TestControlsClosingCall();
373 | }
374 | }
375 |
376 | function TestHangupRTCPeerConnections(peerConnections) {
377 | Object.keys(peerConnections).forEach(function(targetId) {
378 | var peerConnection = peerConnections[targetId];
379 | TestHangupRTCPeerConnection(targetId, peerConnection);
380 | });
381 | }
382 |
383 | function selectControlByName(name) {
384 | return appContainer.querySelector('.controls .btn[name=' + name + ']');
385 | }
386 |
387 | function TestControlsIncomingCall() {
388 | selectControlByName('call_remote').classList.add('hidden');
389 | selectControlByName('hangup_remote').classList.remove('hidden');
390 | appContainer.querySelector('.notice-alone').classList.add('hidden');
391 |
392 | if (isCordova) {
393 | appContainer.style.background = "transparent";
394 | }
395 | }
396 |
397 | function TestControlsOutgoingCall() {
398 | selectControlByName('call_remote').classList.add('hidden');
399 | selectControlByName('hangup_remote').classList.remove('hidden');
400 | appContainer.querySelector('.notice-alone').classList.add('hidden');
401 |
402 | if (isCordova) {
403 | appContainer.style.background = "transparent";
404 | }
405 | }
406 |
407 | function TestControlsClosingCall() {
408 | selectControlByName('hangup_remote').classList.add('hidden');
409 | selectControlByName('call_remote').classList.remove('hidden');
410 | appContainer.querySelector('.notice-alone').classList.remove('hidden');
411 |
412 |
413 | peerVideoEl = appContainer.querySelector('.remote-video');
414 | peerVideoElLoader = appContainer.querySelector('.remote-video-loader');
415 |
416 |
417 | if (peerVideoEl) {
418 | peerVideoEl.classList.add('hidden');
419 | peerVideoEl.srcObject = null;
420 | }
421 |
422 | if (peerVideoElLoader) {
423 | peerVideoElLoader.classList.add('hidden');
424 | }
425 |
426 | if (isCordova) {
427 | appContainer.style.background = "";
428 | }
429 | }
430 |
431 | function TestControls() {
432 |
433 | navigator.mediaDevices.enumerateDevices().then(function(devices) {
434 | console.debug('enumerateDevices', devices);
435 | var canSwitchDevice = devices.filter(function(device) {
436 | return device.kind === 'videoinput';
437 | }).length > 1;
438 |
439 |
440 | if (!canSwitchDevice && typeof navigator.mediaDevices.getDisplayMedia === 'undefined') {
441 | selectControlByName('switch_camera').classList.add('hidden');
442 | }
443 | });
444 |
445 | if (!isCordova || typeof cordova.plugins.iosrtc.selectAudioOutput === 'undefined') {
446 | selectControlByName('speaker').classList.add('hidden');
447 | selectControlByName('earpiece').classList.add('hidden');
448 | }
449 |
450 | if (!isCordova || typeof cordova.plugins.iosrtc.selectAudioOutput === 'undefined') {
451 | selectControlByName('speaker').classList.add('hidden');
452 | selectControlByName('earpiece').classList.add('hidden');
453 | }
454 |
455 | if (!isCordova || typeof cordova.plugins.iosrtc.turnOnSpeaker === 'undefined') {
456 | selectControlByName('speaker').classList.add('hidden');
457 | }
458 |
459 | function handleControlsEvent(event) {
460 | var targetEl = event.target;
461 |
462 | if (!targetEl.classList.contains('btn')) {
463 | targetEl = targetEl.closest('.btn');
464 | }
465 |
466 | if (targetEl && targetEl.classList.contains('btn')) {
467 | var actionName = targetEl.getAttribute('name');
468 | switch (actionName) {
469 | case 'mic_on':
470 | selectControlByName('mic_on').classList.add('hidden');
471 | selectControlByName('mic_off').classList.remove('hidden');
472 | localStream.getAudioTracks().forEach(function(track) {
473 | track.enabled = true;
474 | });
475 | break;
476 | case 'mic_off':
477 | selectControlByName('mic_off').classList.add('hidden');
478 | selectControlByName('mic_on').classList.remove('hidden');
479 | localStream.getAudioTracks().forEach(function(track) {
480 | track.enabled = false;
481 | });
482 | break;
483 | case 'hangup_remote':
484 |
485 | TestControlsClosingCall();
486 |
487 | Object.values(peerConnections).forEach(function(peerConnection) {
488 | TestRemoveStreamToPeerConnection(peerConnection, localStream);
489 | });
490 |
491 | TestHangupRTCPeerConnections(peerConnections);
492 | break;
493 | case 'call_remote':
494 | TestControlsIncomingCall();
495 |
496 | TestHangupRTCPeerConnections(peerConnections);
497 |
498 | TestRTCPeerConnection(localStream);
499 | break;
500 | case 'camera_on':
501 | selectControlByName('camera_on').classList.add('hidden');
502 | selectControlByName('camera_off').classList.remove('hidden');
503 | localVideoEl.classList.remove('hidden');
504 | localStream.getVideoTracks().forEach(function(track) {
505 | track.enabled = true;
506 | });
507 | break;
508 | case 'camera_off':
509 | selectControlByName('camera_off').classList.add('hidden');
510 | selectControlByName('camera_on').classList.remove('hidden');
511 | localVideoEl.classList.add('hidden');
512 | localStream.getVideoTracks().forEach(function(track) {
513 | track.enabled = false;
514 | });
515 | break;
516 | case 'switch_camera':
517 | selectControlByName('switch_camera').classList.toggle('btn-active');
518 |
519 |
520 | localVideoEl.classList.remove('hidden');
521 | localVideoEl.srcObject = null;
522 | /*
523 | localStream.getTracks().forEach(function (track) {
524 | localStream.removeTrack(track);
525 | });
526 | */
527 |
528 | var oldLocalStream = localStream;
529 | TestSwitchCamera().then(function() {
530 |
531 | if (typeof pc1 !== 'undefined' && typeof pc2 !== 'undefined') {
532 |
533 | pc1.createOffer({
534 | iceRestart: true
535 | }).then(function(desc) {
536 |
537 | TestRemoveStreamToPeerConnection(pc1, oldLocalStream);
538 | TestAddStreamToPeerConnection(pc1, localStream);
539 |
540 | return pc1.setLocalDescription(desc).then(function() {
541 | return pc2.setRemoteDescription(desc).then(function() {
542 | return pc2.createAnswer(answerConstraints).then(function(desc) {
543 | return pc2.setLocalDescription(desc).then(function() {
544 | return pc1.setRemoteDescription(desc);
545 | });
546 | });
547 | });
548 | });
549 | }).catch(function(err) {
550 | console.error('TestCallAwnserPeerError', err);
551 | });
552 |
553 | } else {
554 | Object.keys(peerConnections).forEach(function(targetPeerId) {
555 | var peerConnection = peerConnections[targetPeerId];
556 | TestRemoveStreamToPeerConnection(peerConnection, oldLocalStream);
557 | TestAddStreamToPeerConnection(peerConnection, localStream);
558 | return peerConnection.createOffer({
559 | iceRestart: true
560 | }).then(function(desc) {
561 | return peerConnection.setLocalDescription(desc).then(function() {
562 | webSocketSendMessage({
563 | source: peerId,
564 | target: targetPeerId,
565 | type: desc.type,
566 | sdp: desc.sdp
567 | });
568 | });
569 | });
570 | });
571 | }
572 |
573 |
574 | });
575 | break;
576 | case 'speaker':
577 | selectControlByName('earpiece').classList.remove('btn-active');
578 | selectControlByName('speaker').classList.add('btn-active');
579 | cordova.plugins.iosrtc.turnOnSpeaker(true);
580 | break;
581 | case 'earpiece':
582 | selectControlByName('speaker').classList.remove('btn-active');
583 | selectControlByName('earpiece').classList.add('btn-active');
584 | cordova.plugins.iosrtc.selectAudioOutput('earpiece');
585 | break;
586 | case 'mute_remote':
587 | selectControlByName('mute_remote').classList.add('hidden');
588 | selectControlByName('unmute_remote').classList.remove('hidden');
589 | peerStream.getAudioTracks().forEach(function(track) {
590 | if (track.kind == 'audio') {
591 | track.enabled = false;
592 | }
593 | });
594 | break;
595 | case 'unmute_remote':
596 | selectControlByName('unmute_remote').classList.add('hidden');
597 | selectControlByName('mute_remote').classList.remove('hidden');
598 | peerStream.getAudioTracks().forEach(function(track) {
599 | if (track.kind == 'audio') {
600 | track.enabled = true;
601 | }
602 | });
603 | break;
604 | case 'report':
605 | window.location = 'https://github.com/cordova-rtc/cordova-plugin-iosrtc/issues/new';
606 | break;
607 |
608 | default:
609 | console.error('Unknow button name', targetEl);
610 | }
611 | }
612 | }
613 |
614 | appContainer.addEventListener('click', handleControlsEvent, false);
615 | }
616 |
617 | function TestIosRTCSample(event) {
618 |
619 | if (isCordova == false) {
620 | TestControls();
621 |
622 | loadScript(adapterUrl).then(function() {
623 | return TestGetUserMedia().then(function(localStream) {
624 | return TestRTCPeerConnection(localStream);
625 | });
626 | }).catch(function (err) {
627 | console.error(err);
628 | });
629 |
630 | } else {
631 | document.addEventListener('deviceready', function() {
632 |
633 | // Init cordova plugins
634 | if (window.device && window.device.platform == 'iOS') {
635 |
636 | var cordova = window.cordova;
637 |
638 | // Expose WebRTC Globals
639 | if (cordova && cordova.plugins && cordova.plugins.iosrtc) {
640 |
641 | //cordova.plugins.iosrtc.debug.enable('*', true);
642 |
643 | cordova.plugins.iosrtc.registerGlobals();
644 |
645 | cordova.plugins.iosrtc.turnOnSpeaker(true);
646 |
647 | // Implement iosrtc HTML over video trick
648 | document.documentElement.style.background = "transparent";
649 | document.body.style.background = "transparent";
650 | //appContainer.style.background = "transparent";
651 | appContainer.querySelector('.remote-video').style.zIndex = '-1';
652 | }
653 |
654 | // Enable Background audio
655 | if (cordova && cordova.plugins && cordova.plugins.backgroundMode) {
656 | cordova.plugins.backgroundMode.enable();
657 | }
658 | }
659 |
660 | TestControls();
661 | loadScript(adapterUrl).then(function () {
662 | return TestGetUserMedia().then(function(localStream) {
663 | return TestRTCPeerConnection(localStream);
664 | });
665 | }).catch(function (err) {
666 | console.error(err);
667 | });
668 | });
669 | }
670 | }
671 |
672 | if (document.readyState === "complete" || document.readyState === "loaded") {
673 | TestIosRTCSample();
674 | } else {
675 | window.addEventListener("DOMContentLoaded", TestIosRTCSample);
676 | }
677 |
--------------------------------------------------------------------------------
/www/js/index-easyrtc.js:
--------------------------------------------------------------------------------
1 | const EASYRTC_SERVER = 'https://example.com';
2 | const EASYRTC_APP_NAME = 'default';
3 | const EASYRTC_ROOM_NAME = 'test';
4 |
5 | function TestRTCPeerConnection() {
6 | return loadScript('https://unpkg.com/socket.io-client@2.2.0/dist/socket.io.js').then(function () {
7 | return loadScript('https://unpkg.com/open-easyrtc@2.0.5/api/easyrtc.js').then(function () {
8 | return joinRoom({
9 | server: EASYRTC_SERVER,
10 | appName: EASYRTC_APP_NAME,
11 | roomName: EASYRTC_ROOM_NAME
12 | });
13 | });
14 | });
15 | }
16 |
17 | function joinRoom(config) {
18 | // Set server socket url
19 | easyrtc.setSocketUrl(config.server);
20 |
21 | // Handle peer stream
22 | easyrtc.setStreamAcceptor(function (socketId, stream, streamName) {
23 | TestSetPeerStream(stream);
24 | });
25 |
26 | // Register local stream
27 | easyrtc.register3rdPartyLocalMediaStream(localStream, 'default');
28 |
29 | // Connect to easyrtc server
30 | return new Promise(function (resolve, reject) {
31 | easyrtc.connect(config.appName, resolve, reject);
32 | }).then(function () {
33 | return new Promise(function (resolve, reject) {
34 | easyrtc.joinRoom(config.roomName, {}, resolve, reject);
35 | });
36 | });
37 | }
38 |
--------------------------------------------------------------------------------
/www/js/index-janus.js:
--------------------------------------------------------------------------------
1 | /* global RTCPeerConnection */
2 | // jshint unused:false
3 |
4 | // Config
5 | // Note: Change to match your Janus server
6 | var server = "https://janus.conf.meetecho.com/janus";
7 |
8 | // Note: Override common.js adapterVersion and adapterUrl
9 | var adapterVersion = 'latest';
10 | var adapterUrl = "https://webrtc.github.io/adapter/adapter-" + adapterVersion + ".js";
11 |
12 | //
13 | // Test RTCPeerConnection
14 | //
15 |
16 | var janus = null,
17 | echotest = null;
18 |
19 | function TestRTCPeerConnection() {
20 | return loadScript('https://janus.conf.meetecho.com/janus.js').then(function() {
21 |
22 | // Make sure the browser supports WebRTC
23 | if (!Janus.isWebrtcSupported()) {
24 | alert("No WebRTC support... ");
25 | return;
26 | }
27 |
28 | var opaqueId = "echotest-" + Janus.randomString(12);
29 |
30 | // Initialize the library (all console debuggers enabled)
31 | Janus.init({
32 | debug: "all",
33 | callback: function() {
34 |
35 |
36 | // TODO overide unifiedPlan from Janus.init
37 | // Janus.unifiedPlan = false;
38 |
39 | // Create session
40 | janus = new Janus({
41 | server: server,
42 | iceServers: [
43 | {
44 | url: "stun:stun.stunprotocol.org"
45 | }
46 | ],
47 | success: function() {
48 | // Attach to echo test plugin
49 | janus.attach({
50 | plugin: "janus.plugin.echotest",
51 | opaqueId: opaqueId,
52 | success: function(pluginHandle) {
53 | echotest = pluginHandle;
54 | Janus.log("Plugin attached! (" + echotest.getPlugin() + ", id=" + echotest.getId() + ")");
55 |
56 | // Negotiate WebRTC
57 | var body = {
58 | "audio": true,
59 | "video": true
60 | };
61 | Janus.debug("Sending message (" + JSON.stringify(body) + ")");
62 | echotest.send({
63 | "message": body
64 | });
65 |
66 | Janus.debug("Trying a createOffer too (audio/video sendrecv)");
67 | echotest.createOffer({
68 | // No media provided: by default, it's sendrecv for audio and video
69 | //media: { data: false }, // Let's negotiate data channels as well
70 | stream: localStream,
71 | success: function(jsep) {
72 | Janus.debug("Got SDP!");
73 | Janus.debug(jsep);
74 | echotest.send({
75 | "message": body,
76 | "jsep": jsep
77 | });
78 | },
79 | error: function(error) {
80 | Janus.error("WebRTC error:", error);
81 | }
82 | });
83 | },
84 | error: function(error) {
85 | console.error(" -- Error attaching plugin...", error);
86 | },
87 | consentDialog: function(on) {
88 | Janus.debug("Consent dialog should be " + (on ? "on" : "off") + " now");
89 | },
90 | iceState: function(state) {
91 | Janus.log("ICE state changed to " + state);
92 | },
93 | mediaState: function(medium, on) {
94 | Janus.log("Janus " + (on ? "started" : "stopped") + " receiving our " + medium);
95 | },
96 | webrtcState: function(on) {
97 | Janus.log("Janus says our WebRTC PeerConnection is " + (on ? "up" : "down") + " now");
98 | },
99 | slowLink: function(uplink, nacks) {
100 | Janus.warn("Janus reports problems " + (uplink ? "sending" : "receiving") +
101 | " packets on this PeerConnection (" + nacks + " NACKs/s " + (uplink ? "received" : "sent") + ")");
102 | },
103 | onmessage: function(msg, jsep) {
104 | Janus.debug(" ::: Got a message :::");
105 | Janus.debug(msg);
106 | if (jsep !== undefined && jsep !== null) {
107 | Janus.debug("Handling SDP as well...");
108 | Janus.debug(jsep);
109 | echotest.handleRemoteJsep({
110 | jsep: jsep
111 | });
112 | }
113 | },
114 | onlocalstream: function(stream) {
115 | Janus.debug(" ::: Got a local stream :::");
116 | Janus.debug(stream);
117 |
118 | localVideoEl = appContainer.querySelector('.local-video');
119 |
120 | // Note: Expose for debug
121 | localStream = stream;
122 |
123 | // Attach local stream to video element
124 | localVideoEl.srcObject = localStream;
125 | },
126 | onremotestream: function(stream) {
127 | Janus.debug(" ::: Got a remote stream :::");
128 | Janus.debug(stream);
129 | TestSetPeerStream(stream);
130 | },
131 | ondataopen: function(data) {
132 | Janus.log("The DataChannel is available!");
133 | },
134 | ondata: function(data) {
135 | Janus.debug("We got data from the DataChannel! " + data);
136 | },
137 | oncleanup: function() {
138 | Janus.log(" ::: Got a cleanup notification :::");
139 | }
140 | });
141 | },
142 | error: function(error) {
143 | console.error('Janus.error', error);
144 | }
145 | });
146 | }
147 | });
148 | });
149 | }
--------------------------------------------------------------------------------
/www/js/index-jssip.js:
--------------------------------------------------------------------------------
1 | /* global RTCPeerConnection */
2 | // jshint unused:false
3 |
4 | // Config
5 | // Note: Change to match your SIP server and user credentials.
6 |
7 | const FREESWITCH_URL = 'sip.example.com';
8 | const FREESWITCH_PORT = '7443';
9 | const FREESWITCH_USERNAME = 'test';
10 | const FREESWITCH_CREDENTIALS = 'test';
11 |
12 | var sipConfig = {
13 | display_name: 'iOSRTC',
14 | server: 'wss://' + FREESWITCH_URL + ':7443',
15 | uri: 'sip:' + FREESWITCH_USERNAME + '@' + FREESWITCH_URL,
16 | password: FREESWITCH_CREDENTIALS,
17 | authorization_user: null,
18 | realm: FREESWITCH_URL
19 | };
20 |
21 | // Set debug
22 | window.localStorage.setItem('debug', '* -engine* -socket* *ERROR* *WARN*');
23 |
24 | // Note: Select JsSip Implementation (JSSip and SIP.js supported).
25 | var jsSipUrl = "https://cdnjs.cloudflare.com/ajax/libs/jssip/3.1.2/jssip.min.js";
26 | //var jsSipUrl = "https://sipjs.com/download/sip-0.15.6.min.js";
27 |
28 |
29 | //
30 | // Test RTCPeerConnection
31 | //
32 |
33 | var peerConnectionsCandicates = [];
34 |
35 | var webSocket,
36 | webSocketChannel = 'iosrtc',
37 | webSocketRoomID = 'jssip';
38 |
39 | var mediaConstraints = {
40 | video: true,
41 | audio: true
42 | };
43 |
44 | function webSocketSendMessage(msg) {
45 | console.log('webSocketSendMessage', msg);
46 | webSocket.send(JSON.stringify(msg));
47 | }
48 |
49 | var sipUserAgent, sipSession;
50 |
51 | function TestCallOfferPeer(callUri) {
52 |
53 | callUri = callUri || '3500';
54 | // 666, 9196
55 |
56 | console.log('jssip.call', callUri);
57 |
58 | var call;
59 | if (typeof sipUserAgent.invite === 'function') {
60 | call = sipUserAgent.invite.bind(sipUserAgent);
61 | } else {
62 | call = sipUserAgent.call.bind(sipUserAgent);
63 | }
64 |
65 | sipSession = call(callUri, {
66 | //mediaStream: localStream,
67 | // JSSIP
68 | mediaConstraints: mediaConstraints,
69 | eventHandlers: {
70 | 'confirmed': function(data) {
71 | console.info('dialCall.confirmed', data);
72 | },
73 | 'progress': function(data) {
74 | console.info('dialCall.progress', data);
75 | },
76 | 'failed': function(data) {
77 | console.info('dialCall.failed', data);
78 | },
79 | 'ended': function(data) {
80 | console.info('dialCall.ended', data);
81 | }
82 | },
83 | // Sip.JS
84 | sessionDescriptionHandlerOptions: {
85 | constraints: mediaConstraints
86 | },
87 | // Common
88 | pcConfig: {
89 | sdpSemantics: 'plan-b',
90 | tcpMuxPolicy: 'negotiate',
91 | bundlePolicy: 'balanced',
92 | iceServers: [
93 | {
94 | urls: "stun:sip.example.com:3478"
95 | },
96 | {
97 | urls: "turn:sip.example.com:5349",
98 | username: "turnuser",
99 | credential: "turnpwd"
100 | }, {
101 | urls: "turn:sip.example.com:5349?transport=tcp",
102 | username: "turnuser",
103 | credential: "turnpwd"
104 | }
105 | ]
106 | }
107 | });
108 |
109 | sipSession.on('ended', function(e) {
110 | console.debug('sipSession.ended', e);
111 | });
112 |
113 | // Sip.js
114 | if (!isJsSip) {
115 |
116 | sipSession.on('trackAdded', function(e) {
117 |
118 | console.debug('sipSession.trackAdded', e);
119 |
120 | var peerConnection = sipSession.sessionDescriptionHandler.peerConnection;
121 |
122 | // Save peerConnection
123 | peerConnections[sipSession.id] = peerConnection;
124 |
125 | peerConnection.addEventListener('addstream', function(e) {
126 | console.debug('peerConnection.addStream', e);
127 | TestSetPeerStream(e.stream);
128 | });
129 |
130 | // Gets local tracks
131 | var localStream = new MediaStream();
132 | peerConnection.getSenders().forEach(function(sender) {
133 | localStream.addTrack(sender.track || sender);
134 | });
135 |
136 | TestSetLocalStream(localStream);
137 | });
138 | }
139 | }
140 |
141 | function TestCallAwnserPeer() {
142 |
143 | sipSession.answer({
144 | //mediaStream: localStream,
145 | // JSSIP
146 | mediaConstraints: mediaConstraints,
147 | // Sip.JS
148 | media: mediaConstraints,
149 | // Common
150 | pcConfig: peerConnectionConfig,
151 | rtcConstraints: {
152 | mandatory: {
153 | OfferToReceiveVideo: true,
154 | OfferToReceiveAudio: true
155 | }
156 | }
157 | });
158 | }
159 |
160 | function PatchPromiseToCallback(object, prototypeMethod) {
161 |
162 | var originalMethod = object.prototype[prototypeMethod];
163 |
164 | object.prototype[prototypeMethod] = function(arg) {
165 | var success, failure,
166 | args = Array.prototype.slice.call(arguments);
167 |
168 | console.log('PatchPromiseToCallback', prototypeMethod, args);
169 |
170 | var finalArgs = [];
171 | args.forEach(function(arg, idx) {
172 | if (typeof arg === 'function') {
173 | if (!success) {
174 | success = arg;
175 | } else {
176 | failure = arg;
177 | }
178 | } else {
179 | finalArgs.push(arg);
180 | }
181 | });
182 |
183 | return originalMethod.apply(this, finalArgs).then(success).catch(failure);
184 | };
185 | }
186 |
187 | var isJsSip;
188 | function TestRTCPeerConnection() {
189 |
190 | loadScript(jsSipUrl).then(function() {
191 |
192 | isJsSip = typeof JsSIP !== 'undefined';
193 |
194 | var socket;
195 | if (typeof JsSIP === 'undefined') {
196 | JsSIP = SIP;
197 | } else {
198 | socket = new JsSIP.WebSocketInterface(sipConfig.server);
199 | }
200 |
201 | //JsSIP.debug.enable('JsSIP:*');
202 |
203 | sipUserAgent = new JsSIP.UA({
204 | display_name: sipConfig.display_name,
205 | connection_recovery_min_interval: 10,
206 | connection_recovery_max_interval: 60,
207 | sockets: [socket],
208 | session_timers: false,
209 | use_preloaded_route: false,
210 | uri: sipConfig.uri,
211 | password: sipConfig.password,
212 | authorization_user: sipConfig.authorization_user,
213 | realm: sipConfig.realm,
214 | // Sip.JS
215 | hackWssInTransport: true,
216 | authorizationUser: sipConfig.authorization_user,
217 | transportOptions: {
218 | wsServers: sipConfig.server,
219 | },
220 | allowLegacyNotifications: true,
221 | displayName: 'iOSRTC'
222 | });
223 |
224 | sipUserAgent.on('registrationFailed', function(e) {
225 | console.log('jssip.registrationFailed', e);
226 | });
227 |
228 | sipUserAgent.on("registered", function(e) {
229 | console.log('jssip.registered', e);
230 | TestCallOfferPeer();
231 | });
232 |
233 | // JSSIP
234 | if (isJsSip) {
235 |
236 | sipUserAgent.on('unregistered', function(e) {
237 | console.log('jssip.unregistered', e);
238 |
239 | });
240 |
241 | sipUserAgent.on('disconnected', function(e) {
242 | console.log('jssip.disconnected', e);
243 |
244 | });
245 |
246 | sipUserAgent.on("newRTCSession", function(e) {
247 | console.log('jssip.newRTCSession', e);
248 |
249 | var session = e.session; // outgoing call session here
250 |
251 | if (session.direction === 'incoming') {
252 |
253 | session.answer({
254 | // JSSIP
255 | //mediaStream: localStream,
256 | mediaConstraints: mediaConstraints,
257 | // Sip.JS
258 | sessionDescriptionHandlerOptions: {
259 | constraints: mediaConstraints
260 | },
261 | rtcOfferConstraints: {
262 | OfferToReceiveAudio: true,
263 | OfferToReceiveVideo: false
264 | },
265 | rtcAnswerConstraints: {
266 | OfferToSendAudio: false,
267 | OfferToSendVideo: true
268 | }
269 | });
270 |
271 | peerConnection = session.connection;
272 |
273 | peerConnection.addEventListener('addstream', function(e) {
274 | console.debug('peerConnection.addStream', e);
275 | TestSetPeerStream(e.stream);
276 | });
277 |
278 | } else if (session.direction === 'outgoing') {
279 |
280 | peerConnection = session.connection;
281 |
282 | peerConnection.addEventListener('addstream', function(e) {
283 | console.debug('peerConnection.addStream', e);
284 | TestSetPeerStream(e.stream);
285 | });
286 | }
287 |
288 | // Save peerConnection
289 | peerConnections[session.id] = peerConnection;
290 |
291 | // Gets local tracks
292 | session.on('connecting', function () {
293 |
294 | var localStream = new MediaStream();
295 | peerConnection.getSenders().forEach(function(sender) {
296 | localStream.addTrack(sender.track || sender);
297 | });
298 |
299 | TestSetLocalStream(localStream);
300 | });
301 | });
302 | }
303 |
304 | sipUserAgent.start();
305 |
306 | });
307 | }
308 |
--------------------------------------------------------------------------------
/www/js/index-local.js:
--------------------------------------------------------------------------------
1 | /* global RTCPeerConnection */
2 | // jshint unused:false
3 |
4 | //
5 | // Test RTCPeerConnection
6 | //
7 |
8 | var peerConnectionConfig = {
9 | offerToReceiveVideo: true,
10 | offerToReceiveAudio: true,
11 | //iceTransportPolicy: 'relay',
12 | //sdpSemantics: 'unified-plan',
13 | //sdpSemantics: 'plan-b',
14 | //bundlePolicy: 'max-compat',
15 | //rtcpMuxPolicy: 'negotiate',
16 | iceServers: [
17 | {
18 | url: "stun:stun.stunprotocol.org"
19 | }
20 | ]
21 | };
22 |
23 | var peerId = uuid4(),
24 | peerConnections = {},
25 | peerConnectionsCandicates = {};
26 |
27 | var offerConstraints = {
28 | offerToReceiveAudio: true,
29 | offerToReceiveVideo: true
30 | };
31 |
32 | var answerConstraints = {
33 | //iceRestart: true
34 | };
35 |
36 | var pc1, pc2;
37 |
38 |
39 | var useTrackEvent = false; //Object.getOwnPropertyDescriptors(RTCPeerConnection.prototype).ontrack;
40 |
41 | function TestRTCPeerConnection(localStream) {
42 |
43 | // Current you cannot reuse previous RTCPeerConnection
44 | pc1 = new RTCPeerConnection(peerConnectionConfig);
45 | pc2 = new RTCPeerConnection(peerConnectionConfig);
46 |
47 | peerConnections.pc1 = pc1;
48 | peerConnections.pc2 = pc2;
49 |
50 | if (useTrackEvent) {
51 |
52 | localStream.getTracks().forEach(function (track) {
53 | console.log('addTrack', track);
54 | pc1.addTrack(track);
55 | });
56 |
57 | // Note: Deprecated but supported
58 | } else {
59 |
60 | pc1.addStream(localStream);
61 |
62 | // Note: Deprecated Test removeStream
63 | // pc1.removeStream(pc1.getLocalStreams()[0]);<
64 | }
65 |
66 | function onAddIceCandidate(pc, can) {
67 | console.log('addIceCandidate', pc, can);
68 | return can && pc.addIceCandidate(can).catch(function(err) {
69 | console.log('addIceCandidateError', err);
70 | });
71 | }
72 |
73 | pc1.addEventListener('icecandidate', function(e) {
74 | onAddIceCandidate(pc2, e.candidate);
75 | });
76 |
77 | pc2.addEventListener('icecandidate', function(e) {
78 | onAddIceCandidate(pc1, e.candidate);
79 | });
80 |
81 | if (useTrackEvent) {
82 |
83 | var peerStream;
84 |
85 | pc2.addEventListener('track', function(e) {
86 | console.log('pc2.track', e);
87 | var peerStream = e.streams[0] || new MediaStream();
88 | TestSetPeerStream(peerStream);
89 | peerStream.addTrack(e.track);
90 | });
91 | } else {
92 |
93 | pc2.addEventListener('addstream', function(e) {
94 | console.log('pc2.addStream', e);
95 | TestSetPeerStream(e.stream);
96 | });
97 | }
98 |
99 | pc2.addEventListener('removestream', function(e) {
100 | console.log('pc2.removeStream', e);
101 | });
102 |
103 | pc1.addEventListener('iceconnectionstatechange', function(e) {
104 | console.log('pc1.iceConnectionState', e, pc1.iceConnectionState);
105 |
106 | if (pc1.iceConnectionState === 'completed') {
107 | console.log('pc1.getSenders', pc1.getSenders());
108 | console.log('pc2.getReceivers', pc2.getReceivers());
109 | }
110 | });
111 |
112 | pc1.addEventListener('icegatheringstatechange', function(e) {
113 | console.log('pc1.iceGatheringStateChange', e);
114 | });
115 |
116 | // https://stackoverflow.com/questions/48963787/failed-to-set-local-answer-sdp-called-in-wrong-state-kstable
117 | // https://bugs.chromium.org/p/chromium/issues/detail?id=740501
118 | var isNegotiating = false;
119 | pc1.addEventListener('signalingstatechange', function(e) {
120 | console.log('pc1.signalingstatechange', e);
121 | isNegotiating = (pc1.signalingState !== "stable");
122 | });
123 |
124 | pc1.addEventListener('negotiationneeded', function(e) {
125 |
126 | if (isNegotiating) {
127 | // Should not trigger on iosrtc cause of PluginRTCPeerConnection.swift fix
128 | console.log("pc1.negotiatioNeeded", "SKIP nested negotiations");
129 | return;
130 | }
131 | isNegotiating = true;
132 |
133 | console.log('pc1.negotiatioNeeded', e);
134 |
135 | return pc1.createOffer().then(function(d) {
136 | var desc = {
137 | type: d.type,
138 | sdp: d.sdp
139 | };
140 | console.log('pc1.setLocalDescription', desc);
141 | return pc1.setLocalDescription(desc);
142 | }).then(function() {
143 | var desc = {
144 | type: pc1.localDescription.type,
145 | sdp: pc1.localDescription.sdp
146 | };
147 | console.log('pc2.setLocalDescription', desc);
148 | return pc2.setRemoteDescription(desc);
149 | }).then(function() {
150 | console.log('pc2.createAnswer');
151 | return pc2.createAnswer();
152 | }).then(function(d) {
153 | var desc = {
154 | type: d.type,
155 | sdp: d.sdp
156 | };
157 | console.log('pc2.setLocalDescription', desc);
158 | return pc2.setLocalDescription(d);
159 | }).then(function() {
160 | var desc = {
161 | type: pc2.localDescription.type,
162 | sdp: pc2.localDescription.sdp
163 | };
164 | console.log('pc1.setRemoteDescription', desc);
165 | return pc1.setRemoteDescription(desc);
166 | }).then(function() {
167 | TestControlsOutgoingCall();
168 | }).catch(function(err) {
169 | console.log('pc1.createOfferError', err);
170 | });
171 | });
172 | }
--------------------------------------------------------------------------------
/www/js/index-twilio.js:
--------------------------------------------------------------------------------
1 | /* jshint esversion: 6 */
2 | const TWILIO_TOKEN = '';
3 | const TWILIO_ROOM = 'test';
4 | const TWILIO_API_URL = 'https://example.com';
5 | const TWILIO_VERSION = '2.4.0';
6 |
7 | function TestRTCPeerConnection(localStream) {
8 |
9 | // Patch MediaStreamTrack with clone
10 | MediaStreamTrack.prototype.clone = function () {
11 | return this;
12 | };
13 |
14 | return loadScript('https://media.twiliocdn.com/sdk/js/video/releases/' + TWILIO_VERSION + '/twilio-video.js').then(function () {
15 | return getToken().then(function (token) {
16 | return joinRoom(localStream, {
17 | token: TWILIO_TOKEN,
18 | room: TWILIO_ROOM
19 | });
20 | });
21 | });
22 | }
23 |
24 | function getToken() {
25 | if (TWILIO_TOKEN && TWILIO_ROOM) {
26 | return Promise.resolve({
27 | token: TWILIO_TOKEN,
28 | room: TWILIO_ROOM
29 | });
30 | }
31 |
32 | // Example via fetch with XSRF TOKEN
33 | fetch(TWILIO_API_URL + "/auth/session", {
34 | method: 'get'
35 | }).then(function (res) {
36 | var xsrf = res.headers.get('X-XSRF-TOKEN');
37 | var opts = {
38 | room: TWILIO_ROOM,
39 | peerId: 'User-' + Date.now()
40 | };
41 | fetch(TWILIO_API_URL + "/api/twiml/room/token", {
42 | method: 'post',
43 | body: JSON.stringify(opts),
44 | headers: {
45 | 'X-XSRF-TOKEN': xsrf,
46 | 'Accept': 'application/json',
47 | 'Content-Type': 'application/json'
48 | }
49 | }).then(function (res) {
50 | return res.json();
51 | });
52 | });
53 | }
54 |
55 | function _TestGetUserMedia(deviceId) {
56 | Video.createLocalTracks().then(tracks => {
57 | var localMediaContainer = document.querySelector('.local-stream');
58 | tracks.forEach(function(track) {
59 | localMediaContainer.appendChild(track.attach());
60 | });
61 | });
62 | }
63 |
64 | function joinRoom(localStream, config) {
65 |
66 | var Video = Twilio.Video;
67 | var audioTracks = localStream.getAudioTracks().map(track => new Video.LocalAudioTrack(track));
68 | var videoTracks = localStream.getVideoTracks().map(track => new Video.LocalVideoTrack(track));
69 | var tracks = audioTracks.concat(videoTracks);
70 |
71 | Video.connect(config.token, {
72 | name: config.room,
73 | tracks: tracks,
74 | sdpSemantics: 'plan-b',
75 | bundlePolicy: 'max-compat'
76 | }).then(room => {
77 | console.log(`Successfully joined a Room: ${room}`);
78 |
79 | // Attach the Tracks of the Room's Participants.
80 | var remoteMediaContainer = document.querySelector('.remote-stream');
81 | room.participants.forEach(function(participant) {
82 | console.log("Already in Room: '" + participant.identity + "'");
83 | participantConnected(participant, remoteMediaContainer);
84 | });
85 |
86 | room.on('participantConnected', participant => {
87 | console.log(`A remote Participant connected: ${participant}`);
88 | participantConnected(participant);
89 | });
90 |
91 | room.on('participantDisconnected', participant => {
92 | console.log(`A remote Participant connected: ${participant}`);
93 | participantDisconnected(participant);
94 | });
95 |
96 | }, error => {
97 | console.error(`Unable to connect to Room: ${error.message}`);
98 | });
99 |
100 |
101 | function participantConnected(participant) {
102 | console.log('Participant "%s" connected', participant.identity);
103 | var div = document.createElement('div');
104 | div.id = participant.sid;
105 | participant.on('trackSubscribed', (track) => {
106 | trackSubscribed(div, track);
107 | });
108 | participant.on('trackUnsubscribed', trackUnsubscribed);
109 | participant.tracks.forEach(publication => {
110 | if (publication.isSubscribed) {
111 | trackSubscribed(div, publication.track);
112 | }
113 | });
114 |
115 | var remoteMediaContainer = document.querySelector('.remote-stream');
116 | remoteMediaContainer.appendChild(div);
117 | }
118 |
119 | function participantDisconnected(participant) {
120 | console.log('Participant "%s" disconnected', participant.identity);
121 |
122 | var div = document.getElementById(participant.sid);
123 | if (div) {
124 | div.remove();
125 | }
126 | }
127 |
128 | function trackSubscribed(div, track) {
129 | div.appendChild(track.attach());
130 | }
131 |
132 | function trackUnsubscribed(track) {
133 | track.detach().forEach(element => element.remove());
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/www/js/index-websocket.js:
--------------------------------------------------------------------------------
1 | /* global RTCPeerConnection */
2 | const WEBSOCKET_SERVER = 'connect.websocket.in/v3';
3 | const WEBSOCKET_CHANNEL = '1';
4 | const WEBSOCKET_TOKEN = "ySGGACsat7vGEyQdDwrFSu0AoWCae2uNsZe418d7eh6AfJDr9U9nUcgp5MtS";
5 |
6 | //
7 | // RTCPeerConnection Config
8 | // Note: Change to match your TURN and other webRTC settings.
9 | //
10 |
11 | var peerConnectionConfig = {
12 | offerToReceiveVideo: true,
13 | offerToReceiveAudio: true,
14 | //iceTransportPolicy: 'relay',
15 | //sdpSemantics: 'unified-plan',
16 | sdpSemantics: 'plan-b',
17 | bundlePolicy: 'max-compat',
18 | rtcpMuxPolicy: 'negotiate',
19 | iceServers: [
20 | {
21 | urls: "stun:stun.stunprotocol.org"
22 | }
23 | ]
24 | };
25 |
26 | var peerId = uuid4(),
27 | peerConnectionsCandicates = {};
28 |
29 | var optionalsConstraints = [
30 | {"DtlsSrtpKeyAgreement": true},
31 | {"googImprovedWifiBwe": false},
32 | {"googDscp": false},
33 | {"googCpuOveruseDetection": false}
34 | ];
35 |
36 | var offerConstraints = {
37 | offerToReceiveAudio: true,
38 | offerToReceiveVideo: true,
39 | optionals: optionalsConstraints,
40 | };
41 |
42 | var answerConstraints = {
43 | optional: optionalsConstraints,
44 | //iceRestart: true
45 | };
46 |
47 | //
48 | // WebSocket Signaling
49 | //
50 |
51 | var webSocket,
52 | webSocketChannel = WEBSOCKET_CHANNEL,
53 | webSocketToken = WEBSOCKET_TOKEN,
54 | webSocketHostname = WEBSOCKET_SERVER;
55 |
56 | function webSocketSendMessage(msg) {
57 | console.debug('webSocketSendMessage', msg);
58 | webSocket.send(JSON.stringify(msg));
59 | }
60 |
61 | function notifyWebSocketRoom() {
62 | webSocket.send(JSON.stringify({
63 | source: peerId,
64 | type: 'peer'
65 | }));
66 | }
67 |
68 | function joinWebSocketRoom() {
69 |
70 | var webSocketUrl = 'wss://' + webSocketHostname + '/' + webSocketChannel +
71 | '?apiKey=' + webSocketToken;
72 |
73 | webSocket = new WebSocket(webSocketUrl);
74 |
75 | webSocket.onopen = function(e) {
76 | console.debug('socket.open', e);
77 | notifyWebSocketRoom();
78 | };
79 |
80 | webSocket.onclose = function(e) {
81 | webSocket = null;
82 | console.debug('socket.close', e);
83 | TestHangupRTCPeerConnections(peerConnections);
84 | //TestRTCPeerConnection();
85 | };
86 |
87 | webSocket.onmessage = function(e) {
88 | console.debug('socket.message', e);
89 | var data = JSON.parse(e.data);
90 | if (data.type == 'offer' && data.target === peerId) {
91 | TestCallAwnserPeer(data.source, {
92 | type: data.type,
93 | sdp: data.sdp
94 | });
95 | TestControlsOutgoingCall();
96 | } else if (data.type == 'answer' && data.target === peerId) {
97 | if (peerConnections[data.source]) {
98 | TestCallAcceptedByPeer(data.source, {
99 | type: data.type,
100 | sdp: data.sdp
101 | });
102 | TestControlsIncomingCall();
103 | } else {
104 | console.error('TestCallAcceptedByPeer.err', 'Invalid peer', data.source);
105 | }
106 | } else if (data.type == 'candidate' && data.target === peerId) {
107 | TestReceiveCandicate(data.source, data.candidate);
108 | } else if (data.type == 'peer') {
109 | TestCallOfferPeer(data.source);
110 | } else if (data.type == 'accepted') {
111 |
112 | } else if (data.type == 'rejected') {
113 | TestHangupRTCPeerConnection(data.source);
114 | } else if (data.type == 'closed') {
115 | TestHangupRTCPeerConnection(data.source);
116 | }
117 | };
118 |
119 | webSocket.onerror = function(e) {
120 | console.error('socket.error', e);
121 | };
122 | }
123 |
124 | //
125 | //
126 | //
127 |
128 | var preferredCodec;// = 'VP8';
129 |
130 | // Find the line in sdpLines that starts with |prefix|, and, if specified,
131 | // contains |substr| (case-insensitive search).
132 | function findLine(sdpLines, prefix, substr) {
133 | return findLineInRange(sdpLines, 0, -1, prefix, substr);
134 | }
135 |
136 | // Find the line in sdpLines[startLine...endLine - 1] that starts with |prefix|
137 | // and, if specified, contains |substr| (case-insensitive search).
138 | function findLineInRange(sdpLines, startLine, endLine, prefix, substr) {
139 | var realEndLine = endLine !== -1 ? endLine : sdpLines.length;
140 | for (var i = startLine; i < realEndLine; ++i) {
141 | if (sdpLines[i].indexOf(prefix) === 0) {
142 | if (!substr ||
143 | sdpLines[i].toLowerCase().indexOf(substr.toLowerCase()) !== -1) {
144 | return i;
145 | }
146 | }
147 | }
148 | return null;
149 | }
150 |
151 | // Gets the codec payload type from sdp lines.
152 | function getCodecPayloadType(sdpLines, codec) {
153 | var index = findLine(sdpLines, 'a=rtpmap', codec);
154 | return index ? getCodecPayloadTypeFromLine(sdpLines[index]) : null;
155 | }
156 |
157 | // Gets the codec payload type from an a=rtpmap:X line.
158 | function getCodecPayloadTypeFromLine(sdpLine) {
159 | var pattern = new RegExp('a=rtpmap:(\\d+) \\w+\\/\\d+');
160 | var result = sdpLine.match(pattern);
161 | return (result && result.length === 2) ? result[1] : null;
162 | }
163 |
164 | // Returns a new m= line with the specified codec as the first one.
165 | function setDefaultCodec(mLine, payload) {
166 | var elements = mLine.split(' ');
167 |
168 | // Just copy the first three parameters; codec order starts on fourth.
169 | var newLine = elements.slice(0, 3);
170 |
171 | // Put target payload first and copy in the rest.
172 | newLine.push(payload);
173 | for (var i = 3; i < elements.length; i++) {
174 | if (elements[i] !== payload) {
175 | newLine.push(elements[i]);
176 | }
177 | }
178 | return newLine.join(' ');
179 | }
180 |
181 | // Sets |codec| as the default |type| codec if it's present.
182 | // The format of |codec| is 'NAME/RATE', e.g. 'opus/48000'.
183 | function maybePreferCodec(sdp, type, dir, codec) {
184 | var str = type + ' ' + dir + ' codec';
185 | if (!codec) {
186 | console.info('No preference on ' + str + '.');
187 | return sdp;
188 | }
189 |
190 | console.info('Prefer ' + str + ': ' + codec);
191 |
192 | var sdpLines = sdp.split('\r\n');
193 |
194 | // Search for m line.
195 | var mLineIndex = findLine(sdpLines, 'm=', type);
196 | if (mLineIndex === null) {
197 | return sdp;
198 | }
199 |
200 | // If the codec is available, set it as the default in m line.
201 | var payload = getCodecPayloadType(sdpLines, codec);
202 | if (payload) {
203 | sdpLines[mLineIndex] = setDefaultCodec(sdpLines[mLineIndex], payload);
204 | }
205 |
206 | sdp = sdpLines.join('\r\n');
207 | return sdp;
208 | }
209 |
210 | // Promotes |audioSendCodec| to be the first in the m=audio line, if set.
211 | function maybePreferAudioSendCodec(sdp, params) {
212 | return maybePreferCodec(sdp, 'audio', 'send', params.audioSendCodec);
213 | }
214 |
215 | // Promotes |audioRecvCodec| to be the first in the m=audio line, if set.
216 | function maybePreferAudioReceiveCodec(sdp, params) {
217 | return maybePreferCodec(sdp, 'audio', 'receive', params.audioRecvCodec);
218 | }
219 |
220 | // Promotes |videoSendCodec| to be the first in the m=audio line, if set.
221 | function maybePreferVideoSendCodec(sdp, params) {
222 | return maybePreferCodec(sdp, 'video', 'send', params.videoSendCodec);
223 | }
224 |
225 | // Promotes |videoRecvCodec| to be the first in the m=audio line, if set.
226 | function maybePreferVideoReceiveCodec(sdp, params) {
227 | return maybePreferCodec(sdp, 'video', 'receive', params.videoRecvCodec);
228 | }
229 |
230 |
231 | //
232 | // WebRTC Signaling
233 | //
234 |
235 | function TestCallOfferPeer(targetPeerId, constraints) {
236 | var peerConnection = peerConnections[targetPeerId] || new RTCPeerConnection(peerConnectionConfig);
237 |
238 | console.debug('TestCallOfferPeer', targetPeerId, peerConnection.connectionState);
239 | if (!peerConnection.TestListenEvents) {
240 | peerConnection.TestListenEvents = true;
241 | peerConnections[targetPeerId] = peerConnection;
242 | TestRemoveStreamToPeerConnection(peerConnection, localStream);
243 | TestAddStreamToPeerConnection(peerConnection, localStream);
244 | TestListenPeerConnection(targetPeerId, peerConnection);
245 | }
246 |
247 | return peerConnection.createOffer(constraints || offerConstraints).then(function(desc) {
248 |
249 | if (preferredCodec) {
250 | desc.sdp = maybePreferVideoReceiveCodec(desc.sdp, {
251 | videoRecvCodec: preferredCodec
252 | });
253 |
254 | desc.sdp = maybePreferVideoSendCodec(desc.sdp, {
255 | videoSendCodec: preferredCodec
256 | });
257 | }
258 |
259 | return peerConnection.setLocalDescription(desc).then(function() {
260 | webSocketSendMessage({
261 | source: peerId,
262 | target: targetPeerId,
263 | type: desc.type,
264 | sdp: desc.sdp
265 | });
266 | });
267 | }).catch(function(err) {
268 | console.error('TestCallOfferPeerError', err.message, err);
269 | TestHangupRTCPeerConnection(targetPeerId, peerConnection);
270 | });
271 | }
272 |
273 | function TestCallAwnserPeer(targetPeerId, desc, constraints) {
274 | var peerConnection = peerConnections[targetPeerId] || new RTCPeerConnection(peerConnectionConfig);
275 |
276 | console.debug('TestCallAwnserPeer', targetPeerId, peerConnection.connectionState);
277 | if (!peerConnection.TestListenEvents) {
278 | peerConnection.TestListenEvents = true;
279 | peerConnections[targetPeerId] = peerConnection;
280 | TestRemoveStreamToPeerConnection(peerConnection, localStream);
281 | TestAddStreamToPeerConnection(peerConnection, localStream);
282 | TestListenPeerConnection(targetPeerId, peerConnection);
283 | }
284 |
285 | return peerConnection.setRemoteDescription(desc).then(function() {
286 | return peerConnection.createAnswer(constraints || answerConstraints).then(function(desc) {
287 |
288 | if (preferredCodec) {
289 | desc.sdp = maybePreferVideoReceiveCodec(desc.sdp, {
290 | videoRecvCodec: preferredCodec
291 | });
292 |
293 | desc.sdp = maybePreferVideoSendCodec(desc.sdp, {
294 | videoSendCodec: preferredCodec
295 | });
296 | }
297 |
298 | return peerConnection.setLocalDescription(desc).then(function() {
299 |
300 | webSocketSendMessage({
301 | source: peerId,
302 | target: targetPeerId,
303 | type: desc.type,
304 | sdp: desc.sdp
305 | });
306 | });
307 | });
308 | }).catch(function(err) {
309 | console.error('TestCallAwnserPeerError', err);
310 | TestHangupRTCPeerConnection(targetPeerId, peerConnection);
311 | });
312 | }
313 |
314 | function TestProcessCandicate(targetPeerId) {
315 | var peerConnection = peerConnections[targetPeerId];
316 | if (peerConnection && peerConnectionsCandicates[targetPeerId]) {
317 | peerConnectionsCandicates[targetPeerId].forEach(function(can) {
318 | console.debug('peerConnection.addIceCandidate', peerConnection.signalingState, can);
319 | peerConnection.addIceCandidate(can).catch(function(err) {
320 | console.error('peerConnection.addIceCandidateError', err.message, err);
321 | });
322 | });
323 | peerConnectionsCandicates[targetPeerId].length = 0;
324 | }
325 | }
326 |
327 | function TestReceiveCandicate(targetPeerId, can) {
328 | var peerConnectionCandicate = peerConnectionsCandicates[targetPeerId] = peerConnectionsCandicates[targetPeerId] || [];
329 | peerConnectionCandicate.push(can);
330 | TestProcessCandicate(targetPeerId);
331 | }
332 |
333 | function TestListenPeerConnection(targetPeerId, peerConnection) {
334 |
335 | var isNegotiating = false;
336 | var disconnectedTimer;
337 | peerConnection.addEventListener('icecandidate', function(e) {
338 | var candidate = e.candidate;
339 | console.debug('peerConnection.icecandidate', peerConnection, candidate);
340 |
341 | if (candidate && isNegotiating) {
342 | webSocketSendMessage({
343 | source: peerId,
344 | target: targetPeerId,
345 | type: 'candidate',
346 | candidate: candidate
347 | });
348 | }
349 | });
350 |
351 | peerConnection.addEventListener('open', function(e) {
352 | console.debug('peerConnection.open', e);
353 | TestSetPeerStreamLoading(false);
354 | });
355 |
356 | peerConnection.addEventListener('close', function(e) {
357 | console.debug('peerConnection.close', e);
358 | TestHangupRTCPeerConnection(targetPeerId, peerConnection);
359 | });
360 |
361 | peerConnection.addEventListener('track', function(e) {
362 | console.debug('peerConnection.addTrack', e);
363 | });
364 |
365 | peerConnection.addEventListener('addstream', function(e) {
366 | console.debug('peerConnection.addStream', e);
367 | TestSetPeerStream(e.stream);
368 | });
369 |
370 | peerConnection.addEventListener('icegatheringstatechange', function(e) {
371 | console.debug('peerConnection.iceGatheringStateChange', e);
372 | });
373 |
374 | // https://stackoverflow.com/questions/48963787/failed-to-set-local-answer-sdp-called-in-wrong-state-kstable
375 | // https://bugs.chromium.org/p/chromium/issues/detail?id=740501
376 | peerConnection.addEventListener('signalingstatechange', function(e) {
377 | console.debug('peerConnection.signalingstatechange', peerConnection.signalingState, e);
378 |
379 | clearTimeout(disconnectedTimer);
380 | isNegotiating = (peerConnection.signalingState !== "stable" && peerConnection.signalingState !== "closed");
381 |
382 | if (peerConnection.signalingState === 'closed') {
383 | TestHangupRTCPeerConnection(targetPeerId, peerConnection);
384 | }
385 | });
386 |
387 | peerConnection.addEventListener('negotiationneeded', function(e) {
388 | console.debug('peerConnection.negotiationneeded', e);
389 | // TODO https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/negotiationneeded_event
390 | });
391 | }
392 |
393 | function TestCallAcceptedByPeer(targetPeerId, desc) {
394 | var peerConnection = peerConnections[targetPeerId];
395 | return peerConnection.setRemoteDescription(desc).then(function() {
396 | webSocketSendMessage({
397 | source: peerId,
398 | target: targetPeerId,
399 | type: 'accepted'
400 | });
401 | }).catch(function(err) {
402 | console.error('TestCallAcceptedByPeerError', err.message, err);
403 | webSocketSendMessage({
404 | source: peerId,
405 | target: targetPeerId,
406 | type: 'rejected'
407 | });
408 | });
409 | }
410 |
411 |
412 | function TestRTCPeerConnection() {
413 | if (webSocket) {
414 | notifyWebSocketRoom();
415 | } else {
416 | joinWebSocketRoom();
417 | }
418 | }
--------------------------------------------------------------------------------
/www/lib/ios-websocket-hack.js:
--------------------------------------------------------------------------------
1 | /**
2 | * iOS Safari browser makes the Cordova app crash when any plugin method is called
3 | * within a WebSocket event (such as onopen, onmessage, etc). This happens randomly.
4 | * This script overrides the native WebSocket class by making all the events and
5 | * methods to be fired within a setTimeout so event listeners are not fired in the
6 | * buggy WebSocket context.
7 | *
8 | * The issue is described here:
9 | * https://github.com/cordova-rtc/cordova-plugin-iosrtc/issues/12
10 | *
11 | * USAGE:
12 | *
13 | * Just load this script in the HTML of your Cordova iOS app within the first
14 | *