├── App.js
├── README.md
├── android
├── .project
├── .settings
│ └── org.eclipse.buildship.core.prefs
├── app
│ ├── BUCK
│ ├── build.gradle
│ ├── build_defs.bzl
│ ├── proguard-rules.pro
│ └── src
│ │ ├── debug
│ │ └── AndroidManifest.xml
│ │ └── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ └── com
│ │ │ └── myapp
│ │ │ ├── MainActivity.java
│ │ │ └── MainApplication.java
│ │ └── res
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ └── values
│ │ ├── strings.xml
│ │ └── styles.xml
├── build.gradle
├── gradle.properties
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
├── app.json
├── babel.config.js
├── index.js
├── ios
├── Podfile
├── myApp-tvOS
│ └── Info.plist
├── myApp-tvOSTests
│ └── Info.plist
├── myApp.xcodeproj
│ ├── project.pbxproj
│ └── xcshareddata
│ │ └── xcschemes
│ │ ├── myApp-tvOS.xcscheme
│ │ └── myApp.xcscheme
├── myApp
│ ├── AppDelegate.h
│ ├── AppDelegate.m
│ ├── Base.lproj
│ │ └── LaunchScreen.xib
│ ├── Images.xcassets
│ │ ├── AppIcon.appiconset
│ │ │ └── Contents.json
│ │ └── Contents.json
│ ├── Info.plist
│ └── main.m
└── myAppTests
│ ├── Info.plist
│ └── myAppTests.m
├── janus.js
├── metro.config.js
└── package.json
/App.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Sample React Native App
3 | * https://github.com/facebook/react-native
4 | *
5 | * @format
6 | * @flow
7 | */
8 |
9 | import React, { Fragment, Component } from 'react';
10 | import {
11 | TouchableOpacity,
12 | Alert, Button,
13 | StyleSheet,
14 | View,
15 | Text,
16 |
17 | } from 'react-native';
18 |
19 |
20 | import { Janus } from './janus.js';
21 |
22 | import {
23 | RTCPeerConnection,
24 | RTCIceCandidate,
25 | RTCSessionDescription,
26 | RTCView,
27 | MediaStream,
28 | MediaStreamTrack,
29 | mediaDevices
30 | } from 'react-native-webrtc';
31 |
32 | import { Dimensions } from 'react-native';
33 |
34 |
35 | // also support setRemoteDescription, createAnswer, addIceCandidate, onnegotiationneeded, oniceconnectionstatechange, onsignalingstatechange, onaddstream
36 |
37 | const dimensions = Dimensions.get('window')
38 |
39 | const configuration = { "iceServers": [{ "url": "stun:stun.l.google.com:19302" }] };
40 | const pc = new RTCPeerConnection(configuration);
41 | let isFront = false;
42 |
43 | //export default
44 | class App extends React.Component {
45 |
46 |
47 |
48 | constructor(props) {
49 | super(props);
50 |
51 | this.state = { streamUrl: null };
52 | };
53 |
54 |
55 | componentDidMount() {
56 | console.log('componentDidMount');
57 |
58 | mediaDevices.enumerateDevices().then(sourceInfos => {
59 | console.log(sourceInfos);
60 | let videoSourceId;
61 | for (let i = 0; i < sourceInfos.length; i++) {
62 | const sourceInfo = sourceInfos[i];
63 | if (sourceInfo.kind == "videoinput" && sourceInfo.facing == (isFront ? "front" : "back")) {
64 | videoSourceId = sourceInfo.deviceId;
65 | }
66 | }
67 |
68 | mediaDevices.getUserMedia({
69 | audio: true,
70 | video: {
71 | mandatory: {
72 | minWidth: 500, // Provide your own width, height and frame rate here
73 | minHeight: 1200,
74 | minFrameRate: 60
75 | },
76 | facingMode: (isFront ? "user" : "environment"),
77 | optional: (videoSourceId ? [{ sourceId: videoSourceId }] : [])
78 | }
79 | }).then(stream => {
80 | // Got stream!
81 | // this.state.stream = stream;
82 | this.setState(previousState => (
83 | { streamUrl: stream.toURL() }
84 | ))
85 | console.log("Got stream !")
86 | console.log("Stream ID: " + this.state.streamUrl)
87 | }).catch(error => {
88 | // Log error
89 | });
90 | });
91 | }
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 | render() {
100 | return (
101 |
102 | {}
103 |
104 | );
105 | }
106 |
107 | }
108 |
109 |
110 |
111 | pc.createOffer().then(desc => {
112 | pc.setLocalDescription(desc).then(() => {
113 | // Send pc.localDescription to peer
114 | });
115 | });
116 |
117 | pc.onicecandidate = function (event) {
118 | // send event.candidate to peer
119 | };
120 |
121 | var sfutest = null;
122 | let host = "10.1.7.19"
123 | let server = "http://" + host + ":8088/janus"
124 | let backHost = "http://" + host + ":3000/stream"
125 | let pin = null;
126 | let myroom = null;
127 | let myid = null;
128 | let janus = null;
129 |
130 |
131 | Janus.init({
132 | debug: "all", callback: function () {
133 | if (started)
134 | return;
135 | started = true;
136 | }
137 | });
138 |
139 | export default class JanusReactNative extends Component {
140 |
141 | constructor(props) {
142 | super(props);
143 | this.state = {
144 | info: 'Initializing',
145 | status: 'init',
146 | roomID: '',
147 | isFront: true,
148 | selfViewSrc: null,
149 | selfViewSrcKey: null,
150 | remoteList: {},
151 | remoteListPluginHandle: {},
152 | textRoomConnected: false,
153 | textRoomData: [],
154 | textRoomValue: '',
155 | publish: false,
156 | speaker: false,
157 | audioMute: false,
158 | videoMute: false,
159 | visible: false,
160 | buttonText: "Start for Janus !!!"
161 | };
162 | this.janusStart.bind(this);
163 | this.onPressButton.bind(this);
164 | }
165 |
166 |
167 |
168 | componentDidMount() {
169 | }
170 |
171 | janusStart = () => {
172 | this.setState({ visible: true });
173 | janus = new Janus(
174 | {
175 | server: server,
176 | success: () => {
177 | janus.attach(
178 | {
179 | plugin: "janus.plugin.videoroom",
180 | success: (pluginHandle) => {
181 | sfutest = pluginHandle;
182 | this.requestStart().then(this.registerUsername);
183 | // let register = { "request": "join", "room": 1234, "ptype": "publisher", "display": "yanhao", "id": 5035925950 };
184 | // sfutest.send({ "message": register });
185 | },
186 | error: (error) => {
187 | Alert.alert(" -- Error attaching plugin...", error);
188 | },
189 | consentDialog: (on) => {
190 | },
191 | mediaState: (medium, on) => {
192 | },
193 | webrtcState: (on) => {
194 | },
195 | onmessage: (msg, jsep) => {
196 | var event = msg["videoroom"];
197 | if (event != undefined && event != null) {
198 | if (event === "joined") {
199 | myid = msg["id"];
200 | this.publishOwnFeed(true);
201 | this.setState({ visible: false });
202 | if (msg["publishers"] !== undefined && msg["publishers"] !== null) {
203 | var list = msg["publishers"];
204 | for (var f in list) {
205 | var id = list[f]["id"];
206 | var display = list[f]["display"];
207 | // this.newRemoteFeed(id, display)
208 | }
209 | }
210 | } else if (event === "destroyed") {
211 | } else if (event === "event") {
212 | if (msg["publishers"] !== undefined && msg["publishers"] !== null) {
213 | var list = msg["publishers"];
214 | for (var f in list) {
215 | let id = list[f]["id"]
216 | let display = list[f]["display"]
217 | // this.newRemoteFeed(id, display)
218 | }
219 | } else if (msg["leaving"] !== undefined && msg["leaving"] !== null) {
220 | var leaving = msg["leaving"];
221 | var remoteFeed = null;
222 | let numLeaving = parseInt(msg["leaving"])
223 | if (this.state.remoteList.hasOwnProperty(numLeaving)) {
224 | delete this.state.remoteList.numLeaving
225 | this.setState({ remoteList: this.state.remoteList })
226 | this.state.remoteListPluginHandle[numLeaving].detach();
227 | delete this.state.remoteListPluginHandle.numLeaving
228 | }
229 | } else if (msg["unpublished"] !== undefined && msg["unpublished"] !== null) {
230 | var unpublished = msg["unpublished"];
231 | if (unpublished === 'ok') {
232 | sfutest.hangup();
233 | return;
234 | }
235 | let numLeaving = parseInt(msg["unpublished"])
236 | if ('numLeaving' in this.state.remoteList) {
237 | delete this.state.remoteList.numLeaving
238 | this.setState({ remoteList: this.state.remoteList })
239 | this.state.remoteListPluginHandle[numLeaving].detach();
240 | delete this.state.remoteListPluginHandle.numLeaving
241 | }
242 | } else if (msg["error"] !== undefined && msg["error"] !== null) {
243 | }
244 | }
245 | }
246 | if (jsep !== undefined && jsep !== null) {
247 | sfutest.handleRemoteJsep({ jsep: jsep });
248 | }
249 | },
250 | onlocalstream: (stream) => {
251 | this.setState({ selfViewSrc: stream.toURL() });
252 | this.setState({ selfViewSrcKey: Math.floor(Math.random() * 1000) });
253 | this.setState({ status: 'ready', info: 'Please enter or create room ID' });
254 | },
255 | onremotestream: (stream) => {
256 | },
257 | oncleanup: () => {
258 | mystream = null;
259 | }
260 | });
261 | },
262 | error: (error) => {
263 | Alert.alert(" Janus Error", error);
264 | },
265 | destroyed: () => {
266 | // Alert.alert(" Success for End Call ");
267 | this.setState({ publish: false });
268 | }
269 | })
270 | }
271 |
272 | async registerUsername() {
273 | console.log("register user name")
274 | var username = 'yanhao';
275 |
276 | var register = { "request": "join", "room": myroom, "ptype": "publisher", "display": username, "pin": pin, id: myid };
277 | sfutest.send({ "message": register });
278 | var bitrate = 2000 * 1024;
279 | sfutest.send({ "message": { "request": "configure", "bitrate": bitrate } });
280 | }
281 |
282 | publishOwnFeed(useAudio) {
283 | if (!this.state.publish) {
284 | this.setState({ publish: true, buttonText: "Stop"});
285 |
286 | sfutest.createOffer(
287 | {
288 | media: { audioRecv: false, videoRecv: false, audioSend: useAudio, videoSend: true },
289 | success: (jsep) => {
290 | console.log("Create offer : success \n")
291 | var publish = { "request": "configure", "audio": useAudio, "video": true, "bitrate": 5000 * 1024 };
292 | sfutest.send({ "message": publish, "jsep": jsep });
293 | },
294 | error: (error) => {
295 | Alert.alert("WebRTC error:", error);
296 | if (useAudio) {
297 | publishOwnFeed(false);
298 | } else {
299 |
300 | }
301 | }
302 | });
303 | } else {
304 | // this.setState({ publish: false });
305 | // let unpublish = { "request": "unpublish" };
306 | // sfutest.send({"message": unpublish});
307 | }
308 | }
309 |
310 | async requestStart() {
311 | await fetch(backHost, {
312 | cache: "no-cache",
313 | credentials: "omit",
314 | headers: {
315 | Accept: "application/json, text/plain, */*",
316 | "Content-Type": "application/json"
317 | },
318 | method: "POST",
319 | body: JSON.stringify({
320 | login: "yanhao",
321 | passwd: "1234",
322 | roomid: 5555,
323 | request: 'publish'
324 | })
325 | }).then(response => {
326 | return response.json()
327 | }).then(data => {
328 | console.log("parse response")
329 | console.log(data)
330 | if (data.status === "success") {
331 | myroom = data.key.room
332 | pin = data.key.pin
333 | myid = data.key.id
334 | }
335 | })
336 | }
337 |
338 | unpublishOwnFeed(){
339 | if (this.state.publish){
340 | this.setState({buttonText: "Start for Janus !!!"});
341 | let unpublish = {request: "unpublish"};
342 | sfutest.send({message: unpublish});
343 | janus.destroy();
344 | this.setState({ selfViewSrc: null });
345 | }
346 | }
347 |
348 | onPressButton = () => {
349 | if (!this.state.publish){
350 | this.janusStart();
351 | }
352 | else {
353 | this.unpublishOwnFeed();
354 | }
355 | }
356 |
357 |
358 | render() {
359 | return (
360 |
361 |
362 |
363 | {this.state.buttonText}
364 |
365 |
366 |
367 | {this.state.selfViewSrc &&
368 | }
371 |
372 |
373 |
374 |
375 | );
376 | }
377 | }
378 |
379 | const styles = StyleSheet.create({
380 | container: {
381 | paddingTop: 60,
382 | alignItems: 'center'
383 | },
384 | button: {
385 | marginBottom: 30,
386 | width: 260,
387 | alignItems: 'center',
388 | backgroundColor: '#2196F3'
389 | },
390 | buttonText: {
391 | padding: 20,
392 | color: 'white'
393 | }
394 | });
395 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Janus WebRTC React Native module
2 |
3 | ## Usage
4 |
5 | 1. Follow the official instructions to install [React Native]:
6 |
7 | * Install Node 8.3 or newer
8 |
9 | * Run the command in a shell:
10 |
11 | $ npm install -g react-native-cli
12 |
13 | * Install Android Studio or Xcode for mobile application development
14 |
15 | 2. Clone this project. Install essential node modules with npm tool
16 |
17 | $ npm install
18 |
19 | 3. Check the IP address of your computer. Set the parameter of your server host address
20 | (in `App.js` => `let host = "10.1.7.19"`).
21 |
22 | 4. Connect your device to your computer. Build the app and install it on your mobile device.
23 | The following code is for an android device.
24 |
25 | $ react-native start
26 | $ react-native run-android
27 |
28 | 4. Launch Janus server, backend server and Nginx server. (See [janus-demo-docker])
29 |
30 | 5. Open this app in your mobile device. Press the button to communicate with Janus server and backend server.
31 |
32 | 6. Check the log of your Docker server to see if connection is successful.
33 |
34 | [React Native]: https://facebook.github.io/react-native/docs/getting-started
35 |
36 | [janus-demo-docker]: https://github.com/MinesNicaicai/janus-demo-docker
37 |
38 | ## Extension
39 |
40 | The `janus.js` and `App.js` are based on a [project] which is no longer maintained and cannot work due to
41 | obsolete APIs. By updating the use of some APIs, this demo can work now but needs amending.
42 |
43 | [project]: https://github.com/atyenoria/react-native-webrtc-janus-gateway
44 |
45 | Please regularly check the updating of the related library [react-native-webrtc]
46 |
47 | [react-native-webrtc]: https://github.com/react-native-webrtc/react-native-webrtc
--------------------------------------------------------------------------------
/android/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | android
4 | Project android created by Buildship.
5 |
6 |
7 |
8 |
9 | org.eclipse.buildship.core.gradleprojectbuilder
10 |
11 |
12 |
13 |
14 |
15 | org.eclipse.buildship.core.gradleprojectnature
16 |
17 |
18 |
--------------------------------------------------------------------------------
/android/.settings/org.eclipse.buildship.core.prefs:
--------------------------------------------------------------------------------
1 | connection.project.dir=
2 | eclipse.preferences.version=1
3 |
--------------------------------------------------------------------------------
/android/app/BUCK:
--------------------------------------------------------------------------------
1 | # To learn about Buck see [Docs](https://buckbuild.com/).
2 | # To run your application with Buck:
3 | # - install Buck
4 | # - `npm start` - to start the packager
5 | # - `cd android`
6 | # - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"`
7 | # - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck
8 | # - `buck install -r android/app` - compile, install and run application
9 | #
10 |
11 | load(":build_defs.bzl", "create_aar_targets", "create_jar_targets")
12 |
13 | lib_deps = []
14 |
15 | create_aar_targets(glob(["libs/*.aar"]))
16 |
17 | create_jar_targets(glob(["libs/*.jar"]))
18 |
19 | android_library(
20 | name = "all-libs",
21 | exported_deps = lib_deps,
22 | )
23 |
24 | android_library(
25 | name = "app-code",
26 | srcs = glob([
27 | "src/main/java/**/*.java",
28 | ]),
29 | deps = [
30 | ":all-libs",
31 | ":build_config",
32 | ":res",
33 | ],
34 | )
35 |
36 | android_build_config(
37 | name = "build_config",
38 | package = "com.myapp",
39 | )
40 |
41 | android_resource(
42 | name = "res",
43 | package = "com.myapp",
44 | res = "src/main/res",
45 | )
46 |
47 | android_binary(
48 | name = "app",
49 | keystore = "//android/keystores:debug",
50 | manifest = "src/main/AndroidManifest.xml",
51 | package_type = "debug",
52 | deps = [
53 | ":app-code",
54 | ],
55 | )
56 |
--------------------------------------------------------------------------------
/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: "com.android.application"
2 |
3 | import com.android.build.OutputFile
4 |
5 | /**
6 | * The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets
7 | * and bundleReleaseJsAndAssets).
8 | * These basically call `react-native bundle` with the correct arguments during the Android build
9 | * cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the
10 | * bundle directly from the development server. Below you can see all the possible configurations
11 | * and their defaults. If you decide to add a configuration block, make sure to add it before the
12 | * `apply from: "../../node_modules/react-native/react.gradle"` line.
13 | *
14 | * project.ext.react = [
15 | * // the name of the generated asset file containing your JS bundle
16 | * bundleAssetName: "index.android.bundle",
17 | *
18 | * // the entry file for bundle generation
19 | * entryFile: "index.android.js",
20 | *
21 | * // https://facebook.github.io/react-native/docs/performance#enable-the-ram-format
22 | * bundleCommand: "ram-bundle",
23 | *
24 | * // whether to bundle JS and assets in debug mode
25 | * bundleInDebug: false,
26 | *
27 | * // whether to bundle JS and assets in release mode
28 | * bundleInRelease: true,
29 | *
30 | * // whether to bundle JS and assets in another build variant (if configured).
31 | * // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants
32 | * // The configuration property can be in the following formats
33 | * // 'bundleIn${productFlavor}${buildType}'
34 | * // 'bundleIn${buildType}'
35 | * // bundleInFreeDebug: true,
36 | * // bundleInPaidRelease: true,
37 | * // bundleInBeta: true,
38 | *
39 | * // whether to disable dev mode in custom build variants (by default only disabled in release)
40 | * // for example: to disable dev mode in the staging build type (if configured)
41 | * devDisabledInStaging: true,
42 | * // The configuration property can be in the following formats
43 | * // 'devDisabledIn${productFlavor}${buildType}'
44 | * // 'devDisabledIn${buildType}'
45 | *
46 | * // the root of your project, i.e. where "package.json" lives
47 | * root: "../../",
48 | *
49 | * // where to put the JS bundle asset in debug mode
50 | * jsBundleDirDebug: "$buildDir/intermediates/assets/debug",
51 | *
52 | * // where to put the JS bundle asset in release mode
53 | * jsBundleDirRelease: "$buildDir/intermediates/assets/release",
54 | *
55 | * // where to put drawable resources / React Native assets, e.g. the ones you use via
56 | * // require('./image.png')), in debug mode
57 | * resourcesDirDebug: "$buildDir/intermediates/res/merged/debug",
58 | *
59 | * // where to put drawable resources / React Native assets, e.g. the ones you use via
60 | * // require('./image.png')), in release mode
61 | * resourcesDirRelease: "$buildDir/intermediates/res/merged/release",
62 | *
63 | * // by default the gradle tasks are skipped if none of the JS files or assets change; this means
64 | * // that we don't look at files in android/ or ios/ to determine whether the tasks are up to
65 | * // date; if you have any other folders that you want to ignore for performance reasons (gradle
66 | * // indexes the entire tree), add them here. Alternatively, if you have JS files in android/
67 | * // for example, you might want to remove it from here.
68 | * inputExcludes: ["android/**", "ios/**"],
69 | *
70 | * // override which node gets called and with what additional arguments
71 | * nodeExecutableAndArgs: ["node"],
72 | *
73 | * // supply additional arguments to the packager
74 | * extraPackagerArgs: []
75 | * ]
76 | */
77 |
78 | project.ext.react = [
79 | entryFile: "index.js",
80 | enableHermes: false, // clean and rebuild if changing
81 | ]
82 |
83 | apply from: "../../node_modules/react-native/react.gradle"
84 |
85 | /**
86 | * Set this to true to create two separate APKs instead of one:
87 | * - An APK that only works on ARM devices
88 | * - An APK that only works on x86 devices
89 | * The advantage is the size of the APK is reduced by about 4MB.
90 | * Upload all the APKs to the Play Store and people will download
91 | * the correct one based on the CPU architecture of their device.
92 | */
93 | def enableSeparateBuildPerCPUArchitecture = false
94 |
95 | /**
96 | * Run Proguard to shrink the Java bytecode in release builds.
97 | */
98 | def enableProguardInReleaseBuilds = false
99 |
100 | /**
101 | * The preferred build flavor of JavaScriptCore.
102 | *
103 | * For example, to use the international variant, you can use:
104 | * `def jscFlavor = 'org.webkit:android-jsc-intl:+'`
105 | *
106 | * The international variant includes ICU i18n library and necessary data
107 | * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that
108 | * give correct results when using with locales other than en-US. Note that
109 | * this variant is about 6MiB larger per architecture than default.
110 | */
111 | def jscFlavor = 'org.webkit:android-jsc:+'
112 |
113 | /**
114 | * Whether to enable the Hermes VM.
115 | *
116 | * This should be set on project.ext.react and mirrored here. If it is not set
117 | * on project.ext.react, JavaScript will not be compiled to Hermes Bytecode
118 | * and the benefits of using Hermes will therefore be sharply reduced.
119 | */
120 | def enableHermes = project.ext.react.get("enableHermes", false);
121 |
122 | android {
123 | compileSdkVersion rootProject.ext.compileSdkVersion
124 |
125 | compileOptions {
126 | sourceCompatibility JavaVersion.VERSION_1_8
127 | targetCompatibility JavaVersion.VERSION_1_8
128 | }
129 |
130 | defaultConfig {
131 | applicationId "com.myapp"
132 | minSdkVersion rootProject.ext.minSdkVersion
133 | targetSdkVersion rootProject.ext.targetSdkVersion
134 | versionCode 1
135 | versionName "1.0"
136 | }
137 | splits {
138 | abi {
139 | reset()
140 | enable enableSeparateBuildPerCPUArchitecture
141 | universalApk false // If true, also generate a universal APK
142 | include "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
143 | }
144 | }
145 | signingConfigs {
146 | debug {
147 | storeFile file('debug.keystore')
148 | storePassword 'android'
149 | keyAlias 'androiddebugkey'
150 | keyPassword 'android'
151 | }
152 | }
153 | buildTypes {
154 | debug {
155 | signingConfig signingConfigs.debug
156 | }
157 | release {
158 | // Caution! In production, you need to generate your own keystore file.
159 | // see https://facebook.github.io/react-native/docs/signed-apk-android.
160 | signingConfig signingConfigs.debug
161 | minifyEnabled enableProguardInReleaseBuilds
162 | proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
163 | }
164 | }
165 | // applicationVariants are e.g. debug, release
166 | applicationVariants.all { variant ->
167 | variant.outputs.each { output ->
168 | // For each separate APK per architecture, set a unique version code as described here:
169 | // https://developer.android.com/studio/build/configure-apk-splits.html
170 | def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4]
171 | def abi = output.getFilter(OutputFile.ABI)
172 | if (abi != null) { // null for the universal-debug, universal-release variants
173 | output.versionCodeOverride =
174 | versionCodes.get(abi) * 1048576 + defaultConfig.versionCode
175 | }
176 |
177 | }
178 | }
179 |
180 | packagingOptions {
181 | pickFirst '**/armeabi-v7a/libc++_shared.so'
182 | pickFirst '**/x86/libc++_shared.so'
183 | pickFirst '**/arm64-v8a/libc++_shared.so'
184 | pickFirst '**/x86_64/libc++_shared.so'
185 | pickFirst '**/x86/libjsc.so'
186 | pickFirst '**/armeabi-v7a/libjsc.so'
187 | }
188 | }
189 |
190 | dependencies {
191 | implementation fileTree(dir: "libs", include: ["*.jar"])
192 | implementation "com.facebook.react:react-native:+" // From node_modules
193 |
194 | if (enableHermes) {
195 | def hermesPath = "../../node_modules/hermesvm/android/";
196 | debugImplementation files(hermesPath + "hermes-debug.aar")
197 | releaseImplementation files(hermesPath + "hermes-release.aar")
198 | } else {
199 | implementation jscFlavor
200 | }
201 | }
202 |
203 | // Run this once to be able to run the application with BUCK
204 | // puts all compile dependencies into folder libs for BUCK to use
205 | task copyDownloadableDepsToLibs(type: Copy) {
206 | from configurations.compile
207 | into 'libs'
208 | }
209 |
210 | apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
211 |
--------------------------------------------------------------------------------
/android/app/build_defs.bzl:
--------------------------------------------------------------------------------
1 | """Helper definitions to glob .aar and .jar targets"""
2 |
3 | def create_aar_targets(aarfiles):
4 | for aarfile in aarfiles:
5 | name = "aars__" + aarfile[aarfile.rindex("/") + 1:aarfile.rindex(".aar")]
6 | lib_deps.append(":" + name)
7 | android_prebuilt_aar(
8 | name = name,
9 | aar = aarfile,
10 | )
11 |
12 | def create_jar_targets(jarfiles):
13 | for jarfile in jarfiles:
14 | name = "jars__" + jarfile[jarfile.rindex("/") + 1:jarfile.rindex(".jar")]
15 | lib_deps.append(":" + name)
16 | prebuilt_jar(
17 | name = name,
18 | binary_jar = jarfile,
19 | )
20 |
--------------------------------------------------------------------------------
/android/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
--------------------------------------------------------------------------------
/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
22 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/android/app/src/main/java/com/myapp/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.myapp;
2 |
3 | import com.facebook.react.ReactActivity;
4 |
5 | public class MainActivity extends ReactActivity {
6 |
7 | /**
8 | * Returns the name of the main component registered from JavaScript.
9 | * This is used to schedule rendering of the component.
10 | */
11 | @Override
12 | protected String getMainComponentName() {
13 | return "myApp";
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/android/app/src/main/java/com/myapp/MainApplication.java:
--------------------------------------------------------------------------------
1 | package com.myapp;
2 |
3 | import android.app.Application;
4 | import android.util.Log;
5 |
6 | import com.facebook.react.PackageList;
7 | import com.facebook.hermes.reactexecutor.HermesExecutorFactory;
8 | import com.facebook.react.bridge.JavaScriptExecutorFactory;
9 | import com.facebook.react.ReactApplication;
10 | import com.facebook.react.ReactNativeHost;
11 | import com.facebook.react.ReactPackage;
12 | import com.facebook.soloader.SoLoader;
13 | import com.oney.WebRTCModule.WebRTCModulePackage;
14 |
15 | import java.util.List;
16 |
17 | public class MainApplication extends Application implements ReactApplication {
18 |
19 | private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
20 | @Override
21 | public boolean getUseDeveloperSupport() {
22 | return BuildConfig.DEBUG;
23 | }
24 |
25 | @Override
26 | protected List getPackages() {
27 | @SuppressWarnings("UnnecessaryLocalVariable")
28 | List packages = new PackageList(this).getPackages();
29 | // Packages that cannot be autolinked yet can be added manually here, for example:
30 | // packages.add(new MyReactNativePackage());
31 | return packages;
32 | }
33 |
34 | @Override
35 | protected String getJSMainModuleName() {
36 | return "index";
37 | }
38 | };
39 |
40 | @Override
41 | public ReactNativeHost getReactNativeHost() {
42 | return mReactNativeHost;
43 | }
44 |
45 | @Override
46 | public void onCreate() {
47 | super.onCreate();
48 | SoLoader.init(this, /* native exopackage */ false);
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/li-yanhao/janus-webrtc-react-native/0cce2d5cb9440ff43dffb8f45058a234d5a237eb/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/li-yanhao/janus-webrtc-react-native/0cce2d5cb9440ff43dffb8f45058a234d5a237eb/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/li-yanhao/janus-webrtc-react-native/0cce2d5cb9440ff43dffb8f45058a234d5a237eb/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/li-yanhao/janus-webrtc-react-native/0cce2d5cb9440ff43dffb8f45058a234d5a237eb/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/li-yanhao/janus-webrtc-react-native/0cce2d5cb9440ff43dffb8f45058a234d5a237eb/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/li-yanhao/janus-webrtc-react-native/0cce2d5cb9440ff43dffb8f45058a234d5a237eb/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/li-yanhao/janus-webrtc-react-native/0cce2d5cb9440ff43dffb8f45058a234d5a237eb/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/li-yanhao/janus-webrtc-react-native/0cce2d5cb9440ff43dffb8f45058a234d5a237eb/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/li-yanhao/janus-webrtc-react-native/0cce2d5cb9440ff43dffb8f45058a234d5a237eb/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/li-yanhao/janus-webrtc-react-native/0cce2d5cb9440ff43dffb8f45058a234d5a237eb/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/android/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | myApp
3 |
4 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | ext {
5 | buildToolsVersion = "28.0.3"
6 | minSdkVersion = 16
7 | compileSdkVersion = 28
8 | targetSdkVersion = 28
9 | supportLibVersion = "28.0.0"
10 | }
11 | repositories {
12 | google()
13 | jcenter()
14 | }
15 | dependencies {
16 | classpath("com.android.tools.build:gradle:3.4.1")
17 |
18 | // NOTE: Do not place your application dependencies here; they belong
19 | // in the individual module build.gradle files
20 | }
21 | }
22 |
23 | allprojects {
24 | repositories {
25 | mavenLocal()
26 | maven {
27 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
28 | url("$rootDir/../node_modules/react-native/android")
29 | }
30 | maven {
31 | // Android JSC is installed from npm
32 | url("$rootDir/../node_modules/jsc-android/dist")
33 | }
34 |
35 | google()
36 | jcenter()
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
19 |
20 | android.useAndroidX=true
21 | android.enableJetifier=true
22 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/li-yanhao/janus-webrtc-react-native/0cce2d5cb9440ff43dffb8f45058a234d5a237eb/android/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------
/android/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # 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, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 | # Determine the Java command to use to start the JVM.
86 | if [ -n "$JAVA_HOME" ] ; then
87 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
88 | # IBM's JDK on AIX uses strange locations for the executables
89 | JAVACMD="$JAVA_HOME/jre/sh/java"
90 | else
91 | JAVACMD="$JAVA_HOME/bin/java"
92 | fi
93 | if [ ! -x "$JAVACMD" ] ; then
94 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
95 |
96 | Please set the JAVA_HOME variable in your environment to match the
97 | location of your Java installation."
98 | fi
99 | else
100 | JAVACMD="java"
101 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
102 |
103 | Please set the JAVA_HOME variable in your environment to match the
104 | location of your Java installation."
105 | fi
106 |
107 | # Increase the maximum file descriptors if we can.
108 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
109 | MAX_FD_LIMIT=`ulimit -H -n`
110 | if [ $? -eq 0 ] ; then
111 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
112 | MAX_FD="$MAX_FD_LIMIT"
113 | fi
114 | ulimit -n $MAX_FD
115 | if [ $? -ne 0 ] ; then
116 | warn "Could not set maximum file descriptor limit: $MAX_FD"
117 | fi
118 | else
119 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
120 | fi
121 | fi
122 |
123 | # For Darwin, add options to specify how the application appears in the dock
124 | if $darwin; then
125 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
126 | fi
127 |
128 | # For Cygwin, switch paths to Windows format before running java
129 | if $cygwin ; then
130 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
131 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
132 | JAVACMD=`cygpath --unix "$JAVACMD"`
133 |
134 | # We build the pattern for arguments to be converted via cygpath
135 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
136 | SEP=""
137 | for dir in $ROOTDIRSRAW ; do
138 | ROOTDIRS="$ROOTDIRS$SEP$dir"
139 | SEP="|"
140 | done
141 | OURCYGPATTERN="(^($ROOTDIRS))"
142 | # Add a user-defined pattern to the cygpath arguments
143 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
144 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
145 | fi
146 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
147 | i=0
148 | for arg in "$@" ; do
149 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
150 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
151 |
152 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
153 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
154 | else
155 | eval `echo args$i`="\"$arg\""
156 | fi
157 | i=$((i+1))
158 | done
159 | case $i in
160 | (0) set -- ;;
161 | (1) set -- "$args0" ;;
162 | (2) set -- "$args0" "$args1" ;;
163 | (3) set -- "$args0" "$args1" "$args2" ;;
164 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
165 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
166 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
167 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
168 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
169 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
170 | esac
171 | fi
172 |
173 | # Escape application args
174 | save () {
175 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
176 | echo " "
177 | }
178 | APP_ARGS=$(save "$@")
179 |
180 | # Collect all arguments for the java command, following the shell quoting and substitution rules
181 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
182 |
183 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
184 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
185 | cd "$(dirname "$0")"
186 | fi
187 |
188 | exec "$JAVACMD" "$@"
189 |
--------------------------------------------------------------------------------
/android/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem http://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
33 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
34 |
35 | @rem Find java.exe
36 | if defined JAVA_HOME goto findJavaFromJavaHome
37 |
38 | set JAVA_EXE=java.exe
39 | %JAVA_EXE% -version >NUL 2>&1
40 | if "%ERRORLEVEL%" == "0" goto init
41 |
42 | echo.
43 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
44 | echo.
45 | echo Please set the JAVA_HOME variable in your environment to match the
46 | echo location of your Java installation.
47 |
48 | goto fail
49 |
50 | :findJavaFromJavaHome
51 | set JAVA_HOME=%JAVA_HOME:"=%
52 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
53 |
54 | if exist "%JAVA_EXE%" goto init
55 |
56 | echo.
57 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
58 | echo.
59 | echo Please set the JAVA_HOME variable in your environment to match the
60 | echo location of your Java installation.
61 |
62 | goto fail
63 |
64 | :init
65 | @rem Get command-line arguments, handling Windows variants
66 |
67 | if not "%OS%" == "Windows_NT" goto win9xME_args
68 |
69 | :win9xME_args
70 | @rem Slurp the command line arguments.
71 | set CMD_LINE_ARGS=
72 | set _SKIP=2
73 |
74 | :win9xME_args_slurp
75 | if "x%~1" == "x" goto execute
76 |
77 | set CMD_LINE_ARGS=%*
78 |
79 | :execute
80 | @rem Setup the command line
81 |
82 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
83 |
84 | @rem Execute Gradle
85 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
86 |
87 | :end
88 | @rem End local scope for the variables with windows NT shell
89 | if "%ERRORLEVEL%"=="0" goto mainEnd
90 |
91 | :fail
92 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
93 | rem the _cmd.exe /c_ return code!
94 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
95 | exit /b 1
96 |
97 | :mainEnd
98 | if "%OS%"=="Windows_NT" endlocal
99 |
100 | :omega
101 |
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'myApp'
2 | apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings)
3 | include ':WebRTCModule', ':app'
4 | project(':WebRTCModule').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-webrtc/android')
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "myApp",
3 | "displayName": "myApp"
4 | }
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: ['module:metro-react-native-babel-preset'],
3 | };
4 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @format
3 | */
4 |
5 | import {AppRegistry} from 'react-native';
6 | import App from './App';
7 | import {name as appName} from './app.json';
8 |
9 | AppRegistry.registerComponent(appName, () => App);
10 |
--------------------------------------------------------------------------------
/ios/Podfile:
--------------------------------------------------------------------------------
1 | platform :ios, '9.0'
2 | require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
3 |
4 | target 'myApp' do
5 | # Pods for myApp
6 | pod 'React', :path => '../node_modules/react-native/'
7 | pod 'React-Core', :path => '../node_modules/react-native/React'
8 | pod 'React-DevSupport', :path => '../node_modules/react-native/React'
9 | pod 'React-RCTActionSheet', :path => '../node_modules/react-native/Libraries/ActionSheetIOS'
10 | pod 'React-RCTAnimation', :path => '../node_modules/react-native/Libraries/NativeAnimation'
11 | pod 'React-RCTBlob', :path => '../node_modules/react-native/Libraries/Blob'
12 | pod 'React-RCTImage', :path => '../node_modules/react-native/Libraries/Image'
13 | pod 'React-RCTLinking', :path => '../node_modules/react-native/Libraries/LinkingIOS'
14 | pod 'React-RCTNetwork', :path => '../node_modules/react-native/Libraries/Network'
15 | pod 'React-RCTSettings', :path => '../node_modules/react-native/Libraries/Settings'
16 | pod 'React-RCTText', :path => '../node_modules/react-native/Libraries/Text'
17 | pod 'React-RCTVibration', :path => '../node_modules/react-native/Libraries/Vibration'
18 | pod 'React-RCTWebSocket', :path => '../node_modules/react-native/Libraries/WebSocket'
19 |
20 | pod 'React-cxxreact', :path => '../node_modules/react-native/ReactCommon/cxxreact'
21 | pod 'React-jsi', :path => '../node_modules/react-native/ReactCommon/jsi'
22 | pod 'React-jsiexecutor', :path => '../node_modules/react-native/ReactCommon/jsiexecutor'
23 | pod 'React-jsinspector', :path => '../node_modules/react-native/ReactCommon/jsinspector'
24 | pod 'yoga', :path => '../node_modules/react-native/ReactCommon/yoga'
25 |
26 | pod 'DoubleConversion', :podspec => '../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec'
27 | pod 'glog', :podspec => '../node_modules/react-native/third-party-podspecs/glog.podspec'
28 | pod 'Folly', :podspec => '../node_modules/react-native/third-party-podspecs/Folly.podspec'
29 |
30 | target 'myAppTests' do
31 | inherit! :search_paths
32 | # Pods for testing
33 | end
34 |
35 | use_native_modules!
36 | end
37 |
38 | target 'myApp-tvOS' do
39 | # Pods for myApp-tvOS
40 |
41 | target 'myApp-tvOSTests' do
42 | inherit! :search_paths
43 | # Pods for testing
44 | end
45 |
46 | end
47 |
--------------------------------------------------------------------------------
/ios/myApp-tvOS/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | NSAppTransportSecurity
26 |
27 | NSExceptionDomains
28 |
29 | localhost
30 |
31 | NSExceptionAllowsInsecureHTTPLoads
32 |
33 |
34 |
35 |
36 | NSLocationWhenInUseUsageDescription
37 |
38 | UILaunchStoryboardName
39 | LaunchScreen
40 | UIRequiredDeviceCapabilities
41 |
42 | armv7
43 |
44 | UISupportedInterfaceOrientations
45 |
46 | UIInterfaceOrientationPortrait
47 | UIInterfaceOrientationLandscapeLeft
48 | UIInterfaceOrientationLandscapeRight
49 |
50 | UIViewControllerBasedStatusBarAppearance
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/ios/myApp-tvOSTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/ios/myApp.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 00E356F31AD99517003FC87E /* myAppTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* myAppTests.m */; };
11 | 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; };
12 | 13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; };
13 | 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
14 | 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
15 | 2D02E4BC1E0B4A80006451C7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; };
16 | 2D02E4BD1E0B4A84006451C7 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
17 | 2D02E4BF1E0B4AB3006451C7 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
18 | 2DCD954D1E0B4F2C00145EB5 /* myAppTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* myAppTests.m */; };
19 | /* End PBXBuildFile section */
20 |
21 | /* Begin PBXContainerItemProxy section */
22 | 00E356F41AD99517003FC87E /* PBXContainerItemProxy */ = {
23 | isa = PBXContainerItemProxy;
24 | containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */;
25 | proxyType = 1;
26 | remoteGlobalIDString = 13B07F861A680F5B00A75B9A;
27 | remoteInfo = myApp;
28 | };
29 | 2D02E4911E0B4A5D006451C7 /* PBXContainerItemProxy */ = {
30 | isa = PBXContainerItemProxy;
31 | containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */;
32 | proxyType = 1;
33 | remoteGlobalIDString = 2D02E47A1E0B4A5D006451C7;
34 | remoteInfo = "myApp-tvOS";
35 | };
36 | /* End PBXContainerItemProxy section */
37 |
38 | /* Begin PBXFileReference section */
39 | 008F07F21AC5B25A0029DE68 /* main.jsbundle */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = main.jsbundle; sourceTree = ""; };
40 | 00E356EE1AD99517003FC87E /* myAppTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = myAppTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
41 | 00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
42 | 00E356F21AD99517003FC87E /* myAppTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = myAppTests.m; sourceTree = ""; };
43 | 13B07F961A680F5B00A75B9A /* myApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = myApp.app; sourceTree = BUILT_PRODUCTS_DIR; };
44 | 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = myApp/AppDelegate.h; sourceTree = ""; };
45 | 13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = myApp/AppDelegate.m; sourceTree = ""; };
46 | 13B07FB21A68108700A75B9A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; };
47 | 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = myApp/Images.xcassets; sourceTree = ""; };
48 | 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = myApp/Info.plist; sourceTree = ""; };
49 | 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = myApp/main.m; sourceTree = ""; };
50 | 2D02E47B1E0B4A5D006451C7 /* myApp-tvOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "myApp-tvOS.app"; sourceTree = BUILT_PRODUCTS_DIR; };
51 | 2D02E4901E0B4A5D006451C7 /* myApp-tvOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "myApp-tvOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
52 | ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
53 | ED2971642150620600B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS12.0.sdk/System/Library/Frameworks/JavaScriptCore.framework; sourceTree = DEVELOPER_DIR; };
54 | /* End PBXFileReference section */
55 |
56 | /* Begin PBXFrameworksBuildPhase section */
57 | 00E356EB1AD99517003FC87E /* Frameworks */ = {
58 | isa = PBXFrameworksBuildPhase;
59 | buildActionMask = 2147483647;
60 | files = (
61 | );
62 | runOnlyForDeploymentPostprocessing = 0;
63 | };
64 | 13B07F8C1A680F5B00A75B9A /* Frameworks */ = {
65 | isa = PBXFrameworksBuildPhase;
66 | buildActionMask = 2147483647;
67 | files = (
68 | );
69 | runOnlyForDeploymentPostprocessing = 0;
70 | };
71 | 2D02E4781E0B4A5D006451C7 /* Frameworks */ = {
72 | isa = PBXFrameworksBuildPhase;
73 | buildActionMask = 2147483647;
74 | files = (
75 | );
76 | runOnlyForDeploymentPostprocessing = 0;
77 | };
78 | 2D02E48D1E0B4A5D006451C7 /* Frameworks */ = {
79 | isa = PBXFrameworksBuildPhase;
80 | buildActionMask = 2147483647;
81 | files = (
82 | );
83 | runOnlyForDeploymentPostprocessing = 0;
84 | };
85 | /* End PBXFrameworksBuildPhase section */
86 |
87 | /* Begin PBXGroup section */
88 | 00E356EF1AD99517003FC87E /* myAppTests */ = {
89 | isa = PBXGroup;
90 | children = (
91 | 00E356F21AD99517003FC87E /* myAppTests.m */,
92 | 00E356F01AD99517003FC87E /* Supporting Files */,
93 | );
94 | path = myAppTests;
95 | sourceTree = "";
96 | };
97 | 00E356F01AD99517003FC87E /* Supporting Files */ = {
98 | isa = PBXGroup;
99 | children = (
100 | 00E356F11AD99517003FC87E /* Info.plist */,
101 | );
102 | name = "Supporting Files";
103 | sourceTree = "";
104 | };
105 | 13B07FAE1A68108700A75B9A /* myApp */ = {
106 | isa = PBXGroup;
107 | children = (
108 | 008F07F21AC5B25A0029DE68 /* main.jsbundle */,
109 | 13B07FAF1A68108700A75B9A /* AppDelegate.h */,
110 | 13B07FB01A68108700A75B9A /* AppDelegate.m */,
111 | 13B07FB51A68108700A75B9A /* Images.xcassets */,
112 | 13B07FB61A68108700A75B9A /* Info.plist */,
113 | 13B07FB11A68108700A75B9A /* LaunchScreen.xib */,
114 | 13B07FB71A68108700A75B9A /* main.m */,
115 | );
116 | name = myApp;
117 | sourceTree = "";
118 | };
119 | 2D16E6871FA4F8E400B85C8A /* Frameworks */ = {
120 | isa = PBXGroup;
121 | children = (
122 | ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
123 | ED2971642150620600B7C4FE /* JavaScriptCore.framework */,
124 | );
125 | name = Frameworks;
126 | sourceTree = "";
127 | };
128 | 832341AE1AAA6A7D00B99B32 /* Libraries */ = {
129 | isa = PBXGroup;
130 | children = (
131 | );
132 | name = Libraries;
133 | sourceTree = "";
134 | };
135 | 83CBB9F61A601CBA00E9B192 = {
136 | isa = PBXGroup;
137 | children = (
138 | 13B07FAE1A68108700A75B9A /* myApp */,
139 | 832341AE1AAA6A7D00B99B32 /* Libraries */,
140 | 00E356EF1AD99517003FC87E /* myAppTests */,
141 | 83CBBA001A601CBA00E9B192 /* Products */,
142 | 2D16E6871FA4F8E400B85C8A /* Frameworks */,
143 | );
144 | indentWidth = 2;
145 | sourceTree = "";
146 | tabWidth = 2;
147 | usesTabs = 0;
148 | };
149 | 83CBBA001A601CBA00E9B192 /* Products */ = {
150 | isa = PBXGroup;
151 | children = (
152 | 13B07F961A680F5B00A75B9A /* myApp.app */,
153 | 00E356EE1AD99517003FC87E /* myAppTests.xctest */,
154 | 2D02E47B1E0B4A5D006451C7 /* myApp-tvOS.app */,
155 | 2D02E4901E0B4A5D006451C7 /* myApp-tvOSTests.xctest */,
156 | );
157 | name = Products;
158 | sourceTree = "";
159 | };
160 | /* End PBXGroup section */
161 |
162 | /* Begin PBXNativeTarget section */
163 | 00E356ED1AD99517003FC87E /* myAppTests */ = {
164 | isa = PBXNativeTarget;
165 | buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "myAppTests" */;
166 | buildPhases = (
167 | 00E356EA1AD99517003FC87E /* Sources */,
168 | 00E356EB1AD99517003FC87E /* Frameworks */,
169 | 00E356EC1AD99517003FC87E /* Resources */,
170 | );
171 | buildRules = (
172 | );
173 | dependencies = (
174 | 00E356F51AD99517003FC87E /* PBXTargetDependency */,
175 | );
176 | name = myAppTests;
177 | productName = myAppTests;
178 | productReference = 00E356EE1AD99517003FC87E /* myAppTests.xctest */;
179 | productType = "com.apple.product-type.bundle.unit-test";
180 | };
181 | 13B07F861A680F5B00A75B9A /* myApp */ = {
182 | isa = PBXNativeTarget;
183 | buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "myApp" */;
184 | buildPhases = (
185 | FD10A7F022414F080027D42C /* Start Packager */,
186 | 13B07F871A680F5B00A75B9A /* Sources */,
187 | 13B07F8C1A680F5B00A75B9A /* Frameworks */,
188 | 13B07F8E1A680F5B00A75B9A /* Resources */,
189 | 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
190 | );
191 | buildRules = (
192 | );
193 | dependencies = (
194 | );
195 | name = myApp;
196 | productName = "myApp";
197 | productReference = 13B07F961A680F5B00A75B9A /* myApp.app */;
198 | productType = "com.apple.product-type.application";
199 | };
200 | 2D02E47A1E0B4A5D006451C7 /* myApp-tvOS */ = {
201 | isa = PBXNativeTarget;
202 | buildConfigurationList = 2D02E4BA1E0B4A5E006451C7 /* Build configuration list for PBXNativeTarget "myApp-tvOS" */;
203 | buildPhases = (
204 | FD10A7F122414F3F0027D42C /* Start Packager */,
205 | 2D02E4771E0B4A5D006451C7 /* Sources */,
206 | 2D02E4781E0B4A5D006451C7 /* Frameworks */,
207 | 2D02E4791E0B4A5D006451C7 /* Resources */,
208 | 2D02E4CB1E0B4B27006451C7 /* Bundle React Native Code And Images */,
209 | );
210 | buildRules = (
211 | );
212 | dependencies = (
213 | );
214 | name = "myApp-tvOS";
215 | productName = "myApp-tvOS";
216 | productReference = 2D02E47B1E0B4A5D006451C7 /* myApp-tvOS.app */;
217 | productType = "com.apple.product-type.application";
218 | };
219 | 2D02E48F1E0B4A5D006451C7 /* myApp-tvOSTests */ = {
220 | isa = PBXNativeTarget;
221 | buildConfigurationList = 2D02E4BB1E0B4A5E006451C7 /* Build configuration list for PBXNativeTarget "myApp-tvOSTests" */;
222 | buildPhases = (
223 | 2D02E48C1E0B4A5D006451C7 /* Sources */,
224 | 2D02E48D1E0B4A5D006451C7 /* Frameworks */,
225 | 2D02E48E1E0B4A5D006451C7 /* Resources */,
226 | );
227 | buildRules = (
228 | );
229 | dependencies = (
230 | 2D02E4921E0B4A5D006451C7 /* PBXTargetDependency */,
231 | );
232 | name = "myApp-tvOSTests";
233 | productName = "myApp-tvOSTests";
234 | productReference = 2D02E4901E0B4A5D006451C7 /* myApp-tvOSTests.xctest */;
235 | productType = "com.apple.product-type.bundle.unit-test";
236 | };
237 | /* End PBXNativeTarget section */
238 |
239 | /* Begin PBXProject section */
240 | 83CBB9F71A601CBA00E9B192 /* Project object */ = {
241 | isa = PBXProject;
242 | attributes = {
243 | LastUpgradeCheck = 0940;
244 | ORGANIZATIONNAME = Facebook;
245 | TargetAttributes = {
246 | 00E356ED1AD99517003FC87E = {
247 | CreatedOnToolsVersion = 6.2;
248 | TestTargetID = 13B07F861A680F5B00A75B9A;
249 | };
250 | 2D02E47A1E0B4A5D006451C7 = {
251 | CreatedOnToolsVersion = 8.2.1;
252 | ProvisioningStyle = Automatic;
253 | };
254 | 2D02E48F1E0B4A5D006451C7 = {
255 | CreatedOnToolsVersion = 8.2.1;
256 | ProvisioningStyle = Automatic;
257 | TestTargetID = 2D02E47A1E0B4A5D006451C7;
258 | };
259 | };
260 | };
261 | buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "myApp" */;
262 | compatibilityVersion = "Xcode 3.2";
263 | developmentRegion = English;
264 | hasScannedForEncodings = 0;
265 | knownRegions = (
266 | en,
267 | Base,
268 | );
269 | mainGroup = 83CBB9F61A601CBA00E9B192;
270 | productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */;
271 | projectDirPath = "";
272 | projectRoot = "";
273 | targets = (
274 | 13B07F861A680F5B00A75B9A /* myApp */,
275 | 00E356ED1AD99517003FC87E /* myAppTests */,
276 | 2D02E47A1E0B4A5D006451C7 /* myApp-tvOS */,
277 | 2D02E48F1E0B4A5D006451C7 /* myApp-tvOSTests */,
278 | );
279 | };
280 | /* End PBXProject section */
281 |
282 | /* Begin PBXResourcesBuildPhase section */
283 | 00E356EC1AD99517003FC87E /* Resources */ = {
284 | isa = PBXResourcesBuildPhase;
285 | buildActionMask = 2147483647;
286 | files = (
287 | );
288 | runOnlyForDeploymentPostprocessing = 0;
289 | };
290 | 13B07F8E1A680F5B00A75B9A /* Resources */ = {
291 | isa = PBXResourcesBuildPhase;
292 | buildActionMask = 2147483647;
293 | files = (
294 | 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
295 | 13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */,
296 | );
297 | runOnlyForDeploymentPostprocessing = 0;
298 | };
299 | 2D02E4791E0B4A5D006451C7 /* Resources */ = {
300 | isa = PBXResourcesBuildPhase;
301 | buildActionMask = 2147483647;
302 | files = (
303 | 2D02E4BD1E0B4A84006451C7 /* Images.xcassets in Resources */,
304 | );
305 | runOnlyForDeploymentPostprocessing = 0;
306 | };
307 | 2D02E48E1E0B4A5D006451C7 /* Resources */ = {
308 | isa = PBXResourcesBuildPhase;
309 | buildActionMask = 2147483647;
310 | files = (
311 | );
312 | runOnlyForDeploymentPostprocessing = 0;
313 | };
314 | /* End PBXResourcesBuildPhase section */
315 |
316 | /* Begin PBXShellScriptBuildPhase section */
317 | 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */ = {
318 | isa = PBXShellScriptBuildPhase;
319 | buildActionMask = 2147483647;
320 | files = (
321 | );
322 | inputPaths = (
323 | );
324 | name = "Bundle React Native code and images";
325 | outputPaths = (
326 | );
327 | runOnlyForDeploymentPostprocessing = 0;
328 | shellPath = /bin/sh;
329 | shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh";
330 | };
331 | 2D02E4CB1E0B4B27006451C7 /* Bundle React Native Code And Images */ = {
332 | isa = PBXShellScriptBuildPhase;
333 | buildActionMask = 2147483647;
334 | files = (
335 | );
336 | inputPaths = (
337 | );
338 | name = "Bundle React Native Code And Images";
339 | outputPaths = (
340 | );
341 | runOnlyForDeploymentPostprocessing = 0;
342 | shellPath = /bin/sh;
343 | shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh";
344 | };
345 | FD10A7F022414F080027D42C /* Start Packager */ = {
346 | isa = PBXShellScriptBuildPhase;
347 | buildActionMask = 2147483647;
348 | files = (
349 | );
350 | inputFileListPaths = (
351 | );
352 | inputPaths = (
353 | );
354 | name = "Start Packager";
355 | outputFileListPaths = (
356 | );
357 | outputPaths = (
358 | );
359 | runOnlyForDeploymentPostprocessing = 0;
360 | shellPath = /bin/sh;
361 | shellScript = "export RCT_METRO_PORT=\"${RCT_METRO_PORT:=8081}\"\necho \"export RCT_METRO_PORT=${RCT_METRO_PORT}\" > \"${SRCROOT}/../node_modules/react-native/scripts/.packager.env\"\nif [ -z \"${RCT_NO_LAUNCH_PACKAGER+xxx}\" ] ; then\n if nc -w 5 -z localhost ${RCT_METRO_PORT} ; then\n if ! curl -s \"http://localhost:${RCT_METRO_PORT}/status\" | grep -q \"packager-status:running\" ; then\n echo \"Port ${RCT_METRO_PORT} already in use, packager is either not running or not running correctly\"\n exit 2\n fi\n else\n open \"$SRCROOT/../node_modules/react-native/scripts/launchPackager.command\" || echo \"Can't start packager automatically\"\n fi\nfi\n";
362 | showEnvVarsInLog = 0;
363 | };
364 | FD10A7F122414F3F0027D42C /* Start Packager */ = {
365 | isa = PBXShellScriptBuildPhase;
366 | buildActionMask = 2147483647;
367 | files = (
368 | );
369 | inputFileListPaths = (
370 | );
371 | inputPaths = (
372 | );
373 | name = "Start Packager";
374 | outputFileListPaths = (
375 | );
376 | outputPaths = (
377 | );
378 | runOnlyForDeploymentPostprocessing = 0;
379 | shellPath = /bin/sh;
380 | shellScript = "export RCT_METRO_PORT=\"${RCT_METRO_PORT:=8081}\"\necho \"export RCT_METRO_PORT=${RCT_METRO_PORT}\" > \"${SRCROOT}/../node_modules/react-native/scripts/.packager.env\"\nif [ -z \"${RCT_NO_LAUNCH_PACKAGER+xxx}\" ] ; then\n if nc -w 5 -z localhost ${RCT_METRO_PORT} ; then\n if ! curl -s \"http://localhost:${RCT_METRO_PORT}/status\" | grep -q \"packager-status:running\" ; then\n echo \"Port ${RCT_METRO_PORT} already in use, packager is either not running or not running correctly\"\n exit 2\n fi\n else\n open \"$SRCROOT/../node_modules/react-native/scripts/launchPackager.command\" || echo \"Can't start packager automatically\"\n fi\nfi\n";
381 | showEnvVarsInLog = 0;
382 | };
383 | /* End PBXShellScriptBuildPhase section */
384 |
385 | /* Begin PBXSourcesBuildPhase section */
386 | 00E356EA1AD99517003FC87E /* Sources */ = {
387 | isa = PBXSourcesBuildPhase;
388 | buildActionMask = 2147483647;
389 | files = (
390 | 00E356F31AD99517003FC87E /* myAppTests.m in Sources */,
391 | );
392 | runOnlyForDeploymentPostprocessing = 0;
393 | };
394 | 13B07F871A680F5B00A75B9A /* Sources */ = {
395 | isa = PBXSourcesBuildPhase;
396 | buildActionMask = 2147483647;
397 | files = (
398 | 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */,
399 | 13B07FC11A68108700A75B9A /* main.m in Sources */,
400 | );
401 | runOnlyForDeploymentPostprocessing = 0;
402 | };
403 | 2D02E4771E0B4A5D006451C7 /* Sources */ = {
404 | isa = PBXSourcesBuildPhase;
405 | buildActionMask = 2147483647;
406 | files = (
407 | 2D02E4BF1E0B4AB3006451C7 /* main.m in Sources */,
408 | 2D02E4BC1E0B4A80006451C7 /* AppDelegate.m in Sources */,
409 | );
410 | runOnlyForDeploymentPostprocessing = 0;
411 | };
412 | 2D02E48C1E0B4A5D006451C7 /* Sources */ = {
413 | isa = PBXSourcesBuildPhase;
414 | buildActionMask = 2147483647;
415 | files = (
416 | 2DCD954D1E0B4F2C00145EB5 /* myAppTests.m in Sources */,
417 | );
418 | runOnlyForDeploymentPostprocessing = 0;
419 | };
420 | /* End PBXSourcesBuildPhase section */
421 |
422 | /* Begin PBXTargetDependency section */
423 | 00E356F51AD99517003FC87E /* PBXTargetDependency */ = {
424 | isa = PBXTargetDependency;
425 | target = 13B07F861A680F5B00A75B9A /* myApp */;
426 | targetProxy = 00E356F41AD99517003FC87E /* PBXContainerItemProxy */;
427 | };
428 | 2D02E4921E0B4A5D006451C7 /* PBXTargetDependency */ = {
429 | isa = PBXTargetDependency;
430 | target = 2D02E47A1E0B4A5D006451C7 /* myApp-tvOS */;
431 | targetProxy = 2D02E4911E0B4A5D006451C7 /* PBXContainerItemProxy */;
432 | };
433 | /* End PBXTargetDependency section */
434 |
435 | /* Begin PBXVariantGroup section */
436 | 13B07FB11A68108700A75B9A /* LaunchScreen.xib */ = {
437 | isa = PBXVariantGroup;
438 | children = (
439 | 13B07FB21A68108700A75B9A /* Base */,
440 | );
441 | name = LaunchScreen.xib;
442 | path = myApp;
443 | sourceTree = "";
444 | };
445 | /* End PBXVariantGroup section */
446 |
447 | /* Begin XCBuildConfiguration section */
448 | 00E356F61AD99517003FC87E /* Debug */ = {
449 | isa = XCBuildConfiguration;
450 | buildSettings = {
451 | BUNDLE_LOADER = "$(TEST_HOST)";
452 | GCC_PREPROCESSOR_DEFINITIONS = (
453 | "DEBUG=1",
454 | "$(inherited)",
455 | );
456 | INFOPLIST_FILE = myAppTests/Info.plist;
457 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
458 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
459 | OTHER_LDFLAGS = (
460 | "-ObjC",
461 | "-lc++",
462 | "$(inherited)",
463 | );
464 | PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
465 | PRODUCT_NAME = "$(TARGET_NAME)";
466 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/myApp.app/myApp";
467 | };
468 | name = Debug;
469 | };
470 | 00E356F71AD99517003FC87E /* Release */ = {
471 | isa = XCBuildConfiguration;
472 | buildSettings = {
473 | BUNDLE_LOADER = "$(TEST_HOST)";
474 | COPY_PHASE_STRIP = NO;
475 | INFOPLIST_FILE = myAppTests/Info.plist;
476 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
477 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
478 | OTHER_LDFLAGS = (
479 | "-ObjC",
480 | "-lc++",
481 | "$(inherited)",
482 | );
483 | PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
484 | PRODUCT_NAME = "$(TARGET_NAME)";
485 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/myApp.app/myApp";
486 | };
487 | name = Release;
488 | };
489 | 13B07F941A680F5B00A75B9A /* Debug */ = {
490 | isa = XCBuildConfiguration;
491 | buildSettings = {
492 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
493 | CURRENT_PROJECT_VERSION = 1;
494 | DEAD_CODE_STRIPPING = NO;
495 | INFOPLIST_FILE = myApp/Info.plist;
496 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
497 | OTHER_LDFLAGS = (
498 | "$(inherited)",
499 | "-ObjC",
500 | "-lc++",
501 | );
502 | PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
503 | PRODUCT_NAME = myApp;
504 | VERSIONING_SYSTEM = "apple-generic";
505 | };
506 | name = Debug;
507 | };
508 | 13B07F951A680F5B00A75B9A /* Release */ = {
509 | isa = XCBuildConfiguration;
510 | buildSettings = {
511 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
512 | CURRENT_PROJECT_VERSION = 1;
513 | INFOPLIST_FILE = myApp/Info.plist;
514 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
515 | OTHER_LDFLAGS = (
516 | "$(inherited)",
517 | "-ObjC",
518 | "-lc++",
519 | );
520 | PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
521 | PRODUCT_NAME = myApp;
522 | VERSIONING_SYSTEM = "apple-generic";
523 | };
524 | name = Release;
525 | };
526 | 2D02E4971E0B4A5E006451C7 /* Debug */ = {
527 | isa = XCBuildConfiguration;
528 | buildSettings = {
529 | ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image";
530 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
531 | CLANG_ANALYZER_NONNULL = YES;
532 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
533 | CLANG_WARN_INFINITE_RECURSION = YES;
534 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
535 | DEBUG_INFORMATION_FORMAT = dwarf;
536 | ENABLE_TESTABILITY = YES;
537 | GCC_NO_COMMON_BLOCKS = YES;
538 | INFOPLIST_FILE = "myApp-tvOS/Info.plist";
539 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
540 | OTHER_LDFLAGS = (
541 | "$(inherited)",
542 | "-ObjC",
543 | "-lc++",
544 | );
545 | PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.REACT.myApp-tvOS";
546 | PRODUCT_NAME = "$(TARGET_NAME)";
547 | SDKROOT = appletvos;
548 | TARGETED_DEVICE_FAMILY = 3;
549 | TVOS_DEPLOYMENT_TARGET = 9.2;
550 | };
551 | name = Debug;
552 | };
553 | 2D02E4981E0B4A5E006451C7 /* Release */ = {
554 | isa = XCBuildConfiguration;
555 | buildSettings = {
556 | ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image";
557 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
558 | CLANG_ANALYZER_NONNULL = YES;
559 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
560 | CLANG_WARN_INFINITE_RECURSION = YES;
561 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
562 | COPY_PHASE_STRIP = NO;
563 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
564 | GCC_NO_COMMON_BLOCKS = YES;
565 | INFOPLIST_FILE = "myApp-tvOS/Info.plist";
566 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
567 | OTHER_LDFLAGS = (
568 | "$(inherited)",
569 | "-ObjC",
570 | "-lc++",
571 | );
572 | PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.REACT.myApp-tvOS";
573 | PRODUCT_NAME = "$(TARGET_NAME)";
574 | SDKROOT = appletvos;
575 | TARGETED_DEVICE_FAMILY = 3;
576 | TVOS_DEPLOYMENT_TARGET = 9.2;
577 | };
578 | name = Release;
579 | };
580 | 2D02E4991E0B4A5E006451C7 /* Debug */ = {
581 | isa = XCBuildConfiguration;
582 | buildSettings = {
583 | BUNDLE_LOADER = "$(TEST_HOST)";
584 | CLANG_ANALYZER_NONNULL = YES;
585 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
586 | CLANG_WARN_INFINITE_RECURSION = YES;
587 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
588 | DEBUG_INFORMATION_FORMAT = dwarf;
589 | ENABLE_TESTABILITY = YES;
590 | GCC_NO_COMMON_BLOCKS = YES;
591 | INFOPLIST_FILE = "myApp-tvOSTests/Info.plist";
592 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
593 | OTHER_LDFLAGS = (
594 | "$(inherited)",
595 | "-ObjC",
596 | "-lc++",
597 | );
598 | PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.REACT.myApp-tvOSTests";
599 | PRODUCT_NAME = "$(TARGET_NAME)";
600 | SDKROOT = appletvos;
601 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/myApp-tvOS.app/myApp-tvOS";
602 | TVOS_DEPLOYMENT_TARGET = 10.1;
603 | };
604 | name = Debug;
605 | };
606 | 2D02E49A1E0B4A5E006451C7 /* Release */ = {
607 | isa = XCBuildConfiguration;
608 | buildSettings = {
609 | BUNDLE_LOADER = "$(TEST_HOST)";
610 | CLANG_ANALYZER_NONNULL = YES;
611 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
612 | CLANG_WARN_INFINITE_RECURSION = YES;
613 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
614 | COPY_PHASE_STRIP = NO;
615 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
616 | GCC_NO_COMMON_BLOCKS = YES;
617 | INFOPLIST_FILE = "myApp-tvOSTests/Info.plist";
618 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
619 | OTHER_LDFLAGS = (
620 | "$(inherited)",
621 | "-ObjC",
622 | "-lc++",
623 | );
624 | PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.REACT.myApp-tvOSTests";
625 | PRODUCT_NAME = "$(TARGET_NAME)";
626 | SDKROOT = appletvos;
627 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/myApp-tvOS.app/myApp-tvOS";
628 | TVOS_DEPLOYMENT_TARGET = 10.1;
629 | };
630 | name = Release;
631 | };
632 | 83CBBA201A601CBA00E9B192 /* Debug */ = {
633 | isa = XCBuildConfiguration;
634 | buildSettings = {
635 | ALWAYS_SEARCH_USER_PATHS = NO;
636 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
637 | CLANG_CXX_LIBRARY = "libc++";
638 | CLANG_ENABLE_MODULES = YES;
639 | CLANG_ENABLE_OBJC_ARC = YES;
640 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
641 | CLANG_WARN_BOOL_CONVERSION = YES;
642 | CLANG_WARN_COMMA = YES;
643 | CLANG_WARN_CONSTANT_CONVERSION = YES;
644 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
645 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
646 | CLANG_WARN_EMPTY_BODY = YES;
647 | CLANG_WARN_ENUM_CONVERSION = YES;
648 | CLANG_WARN_INFINITE_RECURSION = YES;
649 | CLANG_WARN_INT_CONVERSION = YES;
650 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
651 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
652 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
653 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
654 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
655 | CLANG_WARN_STRICT_PROTOTYPES = YES;
656 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
657 | CLANG_WARN_UNREACHABLE_CODE = YES;
658 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
659 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
660 | COPY_PHASE_STRIP = NO;
661 | ENABLE_STRICT_OBJC_MSGSEND = YES;
662 | ENABLE_TESTABILITY = YES;
663 | GCC_C_LANGUAGE_STANDARD = gnu99;
664 | GCC_DYNAMIC_NO_PIC = NO;
665 | GCC_NO_COMMON_BLOCKS = YES;
666 | GCC_OPTIMIZATION_LEVEL = 0;
667 | GCC_PREPROCESSOR_DEFINITIONS = (
668 | "DEBUG=1",
669 | "$(inherited)",
670 | );
671 | GCC_SYMBOLS_PRIVATE_EXTERN = NO;
672 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
673 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
674 | GCC_WARN_UNDECLARED_SELECTOR = YES;
675 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
676 | GCC_WARN_UNUSED_FUNCTION = YES;
677 | GCC_WARN_UNUSED_VARIABLE = YES;
678 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
679 | MTL_ENABLE_DEBUG_INFO = YES;
680 | ONLY_ACTIVE_ARCH = YES;
681 | SDKROOT = iphoneos;
682 | };
683 | name = Debug;
684 | };
685 | 83CBBA211A601CBA00E9B192 /* Release */ = {
686 | isa = XCBuildConfiguration;
687 | buildSettings = {
688 | ALWAYS_SEARCH_USER_PATHS = NO;
689 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
690 | CLANG_CXX_LIBRARY = "libc++";
691 | CLANG_ENABLE_MODULES = YES;
692 | CLANG_ENABLE_OBJC_ARC = YES;
693 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
694 | CLANG_WARN_BOOL_CONVERSION = YES;
695 | CLANG_WARN_COMMA = YES;
696 | CLANG_WARN_CONSTANT_CONVERSION = YES;
697 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
698 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
699 | CLANG_WARN_EMPTY_BODY = YES;
700 | CLANG_WARN_ENUM_CONVERSION = YES;
701 | CLANG_WARN_INFINITE_RECURSION = YES;
702 | CLANG_WARN_INT_CONVERSION = YES;
703 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
704 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
705 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
706 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
707 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
708 | CLANG_WARN_STRICT_PROTOTYPES = YES;
709 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
710 | CLANG_WARN_UNREACHABLE_CODE = YES;
711 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
712 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
713 | COPY_PHASE_STRIP = YES;
714 | ENABLE_NS_ASSERTIONS = NO;
715 | ENABLE_STRICT_OBJC_MSGSEND = YES;
716 | GCC_C_LANGUAGE_STANDARD = gnu99;
717 | GCC_NO_COMMON_BLOCKS = YES;
718 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
719 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
720 | GCC_WARN_UNDECLARED_SELECTOR = YES;
721 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
722 | GCC_WARN_UNUSED_FUNCTION = YES;
723 | GCC_WARN_UNUSED_VARIABLE = YES;
724 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
725 | MTL_ENABLE_DEBUG_INFO = NO;
726 | SDKROOT = iphoneos;
727 | VALIDATE_PRODUCT = YES;
728 | };
729 | name = Release;
730 | };
731 | /* End XCBuildConfiguration section */
732 |
733 | /* Begin XCConfigurationList section */
734 | 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "myAppTests" */ = {
735 | isa = XCConfigurationList;
736 | buildConfigurations = (
737 | 00E356F61AD99517003FC87E /* Debug */,
738 | 00E356F71AD99517003FC87E /* Release */,
739 | );
740 | defaultConfigurationIsVisible = 0;
741 | defaultConfigurationName = Release;
742 | };
743 | 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "myApp" */ = {
744 | isa = XCConfigurationList;
745 | buildConfigurations = (
746 | 13B07F941A680F5B00A75B9A /* Debug */,
747 | 13B07F951A680F5B00A75B9A /* Release */,
748 | );
749 | defaultConfigurationIsVisible = 0;
750 | defaultConfigurationName = Release;
751 | };
752 | 2D02E4BA1E0B4A5E006451C7 /* Build configuration list for PBXNativeTarget "myApp-tvOS" */ = {
753 | isa = XCConfigurationList;
754 | buildConfigurations = (
755 | 2D02E4971E0B4A5E006451C7 /* Debug */,
756 | 2D02E4981E0B4A5E006451C7 /* Release */,
757 | );
758 | defaultConfigurationIsVisible = 0;
759 | defaultConfigurationName = Release;
760 | };
761 | 2D02E4BB1E0B4A5E006451C7 /* Build configuration list for PBXNativeTarget "myApp-tvOSTests" */ = {
762 | isa = XCConfigurationList;
763 | buildConfigurations = (
764 | 2D02E4991E0B4A5E006451C7 /* Debug */,
765 | 2D02E49A1E0B4A5E006451C7 /* Release */,
766 | );
767 | defaultConfigurationIsVisible = 0;
768 | defaultConfigurationName = Release;
769 | };
770 | 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "myApp" */ = {
771 | isa = XCConfigurationList;
772 | buildConfigurations = (
773 | 83CBBA201A601CBA00E9B192 /* Debug */,
774 | 83CBBA211A601CBA00E9B192 /* Release */,
775 | );
776 | defaultConfigurationIsVisible = 0;
777 | defaultConfigurationName = Release;
778 | };
779 | /* End XCConfigurationList section */
780 | };
781 | rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */;
782 | }
783 |
--------------------------------------------------------------------------------
/ios/myApp.xcodeproj/xcshareddata/xcschemes/myApp-tvOS.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
29 |
35 |
36 |
37 |
43 |
49 |
50 |
51 |
52 |
53 |
58 |
59 |
61 |
67 |
68 |
69 |
70 |
71 |
77 |
78 |
79 |
80 |
81 |
82 |
92 |
94 |
100 |
101 |
102 |
103 |
104 |
105 |
111 |
113 |
119 |
120 |
121 |
122 |
124 |
125 |
128 |
129 |
130 |
--------------------------------------------------------------------------------
/ios/myApp.xcodeproj/xcshareddata/xcschemes/myApp.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
29 |
35 |
36 |
37 |
43 |
49 |
50 |
51 |
52 |
53 |
58 |
59 |
61 |
67 |
68 |
69 |
70 |
71 |
77 |
78 |
79 |
80 |
81 |
82 |
92 |
94 |
100 |
101 |
102 |
103 |
104 |
105 |
111 |
113 |
119 |
120 |
121 |
122 |
124 |
125 |
128 |
129 |
130 |
--------------------------------------------------------------------------------
/ios/myApp/AppDelegate.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) Facebook, Inc. and its affiliates.
3 | *
4 | * This source code is licensed under the MIT license found in the
5 | * LICENSE file in the root directory of this source tree.
6 | */
7 |
8 | #import
9 | #import
10 |
11 | @interface AppDelegate : UIResponder
12 |
13 | @property (nonatomic, strong) UIWindow *window;
14 |
15 | @end
16 |
--------------------------------------------------------------------------------
/ios/myApp/AppDelegate.m:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) Facebook, Inc. and its affiliates.
3 | *
4 | * This source code is licensed under the MIT license found in the
5 | * LICENSE file in the root directory of this source tree.
6 | */
7 |
8 | #import "AppDelegate.h"
9 |
10 | #import
11 | #import
12 | #import
13 |
14 | @implementation AppDelegate
15 |
16 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
17 | {
18 | RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
19 | RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
20 | moduleName:@"myApp"
21 | initialProperties:nil];
22 |
23 | rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];
24 |
25 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
26 | UIViewController *rootViewController = [UIViewController new];
27 | rootViewController.view = rootView;
28 | self.window.rootViewController = rootViewController;
29 | [self.window makeKeyAndVisible];
30 | return YES;
31 | }
32 |
33 | - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
34 | {
35 | #if DEBUG
36 | return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
37 | #else
38 | return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
39 | #endif
40 | }
41 |
42 | @end
43 |
--------------------------------------------------------------------------------
/ios/myApp/Base.lproj/LaunchScreen.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
21 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/ios/myApp/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "29x29",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "29x29",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "40x40",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "40x40",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "60x60",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "60x60",
31 | "scale" : "3x"
32 | }
33 | ],
34 | "info" : {
35 | "version" : 1,
36 | "author" : "xcode"
37 | }
38 | }
--------------------------------------------------------------------------------
/ios/myApp/Images.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/ios/myApp/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | myApp
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | 1
25 | LSRequiresIPhoneOS
26 |
27 | NSAppTransportSecurity
28 |
29 | NSAllowsArbitraryLoads
30 |
31 | NSExceptionDomains
32 |
33 | localhost
34 |
35 | NSExceptionAllowsInsecureHTTPLoads
36 |
37 |
38 |
39 |
40 | NSLocationWhenInUseUsageDescription
41 |
42 | UILaunchStoryboardName
43 | LaunchScreen
44 | UIRequiredDeviceCapabilities
45 |
46 | armv7
47 |
48 | UISupportedInterfaceOrientations
49 |
50 | UIInterfaceOrientationPortrait
51 | UIInterfaceOrientationLandscapeLeft
52 | UIInterfaceOrientationLandscapeRight
53 |
54 | UIViewControllerBasedStatusBarAppearance
55 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/ios/myApp/main.m:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) Facebook, Inc. and its affiliates.
3 | *
4 | * This source code is licensed under the MIT license found in the
5 | * LICENSE file in the root directory of this source tree.
6 | */
7 |
8 | #import
9 |
10 | #import "AppDelegate.h"
11 |
12 | int main(int argc, char * argv[]) {
13 | @autoreleasepool {
14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/ios/myAppTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/ios/myAppTests/myAppTests.m:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) Facebook, Inc. and its affiliates.
3 | *
4 | * This source code is licensed under the MIT license found in the
5 | * LICENSE file in the root directory of this source tree.
6 | */
7 |
8 | #import
9 | #import
10 |
11 | #import
12 | #import
13 |
14 | #define TIMEOUT_SECONDS 600
15 | #define TEXT_TO_LOOK_FOR @"Welcome to React Native!"
16 |
17 | @interface myAppTests : XCTestCase
18 |
19 | @end
20 |
21 | @implementation myAppTests
22 |
23 | - (BOOL)findSubviewInView:(UIView *)view matching:(BOOL(^)(UIView *view))test
24 | {
25 | if (test(view)) {
26 | return YES;
27 | }
28 | for (UIView *subview in [view subviews]) {
29 | if ([self findSubviewInView:subview matching:test]) {
30 | return YES;
31 | }
32 | }
33 | return NO;
34 | }
35 |
36 | - (void)testRendersWelcomeScreen
37 | {
38 | UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController];
39 | NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS];
40 | BOOL foundElement = NO;
41 |
42 | __block NSString *redboxError = nil;
43 | RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) {
44 | if (level >= RCTLogLevelError) {
45 | redboxError = message;
46 | }
47 | });
48 |
49 | while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) {
50 | [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
51 | [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
52 |
53 | foundElement = [self findSubviewInView:vc.view matching:^BOOL(UIView *view) {
54 | if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) {
55 | return YES;
56 | }
57 | return NO;
58 | }];
59 | }
60 |
61 | RCTSetLogFunction(RCTDefaultLogFunction);
62 |
63 | XCTAssertNil(redboxError, @"RedBox error: %@", redboxError);
64 | XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS);
65 | }
66 |
67 |
68 | @end
69 |
--------------------------------------------------------------------------------
/janus.js:
--------------------------------------------------------------------------------
1 | /*
2 | The MIT License (MIT)
3 |
4 | Copyright (c) 2016 Meetecho
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining
7 | a copy of this software and associated documentation files (the "Software"),
8 | to deal in the Software without restriction, including without limitation
9 | the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 | and/or sell copies of the Software, and to permit persons to whom the
11 | Software is furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included
14 | in all copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 | OTHER DEALINGS IN THE SOFTWARE. */
23 |
24 | import {
25 | RTCPeerConnection,
26 | RTCMediaStream,
27 | RTCIceCandidate,
28 | RTCSessionDescription,
29 | RTCView,
30 | mediaDevices,
31 | } from 'react-native-webrtc';
32 |
33 | import {
34 | Alert
35 | } from 'react-native';
36 |
37 |
38 | // List of sessions
39 | Janus.sessions = {};
40 |
41 | // Screensharing Chrome Extension ID
42 | Janus.extensionId = "hapfgfdkleiggjjpfpenajgdnfckjpaj";
43 |
44 |
45 | Janus.noop = function () { };
46 |
47 | // Initialization
48 | Janus.init = function (options) {
49 | options = options || {};
50 | options.callback = (typeof options.callback == "function") ? options.callback : Janus.noop;
51 | if (Janus.initDone === true) {
52 | // Already initialized
53 | options.callback();
54 | } else {
55 | if (typeof console == "undefined" || typeof console.log == "undefined")
56 | console = { log: function () { } };
57 | // Console logging (all debugging disabled by default)
58 | Janus.trace = Janus.noop;
59 | Janus.debug = Janus.noop;
60 | Janus.log = Janus.noop;
61 | Janus.warn = Janus.noop;
62 | Janus.error = Janus.noop;
63 | if (options.debug === true || options.debug === "all") {
64 | // Enable all debugging levels
65 | Janus.trace = console.trace.bind(console);
66 | Janus.debug = console.debug.bind(console);
67 | Janus.log = console.log.bind(console);
68 | Janus.warn = console.warn.bind(console);
69 | Janus.error = console.error.bind(console);
70 | } else if (Array.isArray(options.debug)) {
71 | for (var i in options.debug) {
72 | var d = options.debug[i];
73 | switch (d) {
74 | case "trace":
75 | Janus.trace = console.trace.bind(console);
76 | break;
77 | case "debug":
78 | Janus.debug = console.debug.bind(console);
79 | break;
80 | case "log":
81 | Janus.log = console.log.bind(console);
82 | break;
83 | case "warn":
84 | Janus.warn = console.warn.bind(console);
85 | break;
86 | case "error":
87 | Janus.error = console.error.bind(console);
88 | break;
89 | default:
90 | console.error("Unknown debugging option '" + d + "' (supported: 'trace', 'debug', 'log', warn', 'error')");
91 | break;
92 | }
93 | }
94 | }
95 | Janus.log("Initializing library");
96 | // Helper method to enumerate devices
97 | Janus.listDevices = function (callback) {
98 | callback = (typeof callback == "function") ? callback : Janus.noop;
99 | if (navigator.mediaDevices) {
100 | getUserMedia({ audio: true, video: true }, function (stream) {
101 | navigator.mediaDevices.enumerateDevices().then(function (devices) {
102 | Janus.debug(devices);
103 | callback(devices);
104 | // Get rid of the now useless stream
105 | try {
106 | stream.stop();
107 | } catch (e) { }
108 | try {
109 | var tracks = stream.getTracks();
110 | for (var i in tracks) {
111 | var mst = tracks[i];
112 | if (mst !== null && mst !== undefined)
113 | mst.stop();
114 | }
115 | } catch (e) { }
116 | });
117 | }, function (err) {
118 | Janus.error(err);
119 | callback([]);
120 | });
121 | } else {
122 | Janus.warn("navigator.mediaDevices unavailable");
123 | callback([]);
124 | }
125 | }
126 | // Prepare a helper method to send AJAX requests in a syntax similar to jQuery (at least for what we care)
127 | Janus.ajax = function (params) {
128 | // Check params
129 | if (params === null || params === undefined)
130 | return;
131 | params.success = (typeof params.success == "function") ? params.success : Janus.noop;
132 | params.error = (typeof params.error == "function") ? params.error : Janus.noop;
133 | // Make sure there's an URL
134 | if (params.url === null || params.url === undefined) {
135 | Janus.error('Missing url', params.url);
136 | params.error(null, -1, 'Missing url');
137 | return;
138 | }
139 | // Validate async
140 | params.async = (params.async === null || params.async === undefined) ? true : (params.async === true);
141 | Janus.log(params);
142 | // IE doesn't even know what WebRTC is, so no polyfill needed
143 | var XHR = new XMLHttpRequest();
144 | XHR.open(params.type, params.url, params.async);
145 | if (params.contentType !== null && params.contentType !== undefined)
146 | XHR.setRequestHeader('Content-type', params.contentType);
147 | if (params.async) {
148 | XHR.onreadystatechange = function () {
149 | if (XHR.readyState != 4)
150 | return;
151 | if (XHR.status !== 200) {
152 | // Got an error?
153 | if (XHR.status === 0)
154 | XHR.status = "error";
155 | params.error(XHR, XHR.status, "");
156 | return;
157 | }
158 | // Got payload
159 | params.success(JSON.parse(XHR.responseText));
160 | };
161 | }
162 | try {
163 | XHR.send(params.data);
164 | if (!params.async) {
165 | if (XHR.status !== 200) {
166 | // Got an error?
167 | if (XHR.status === 0)
168 | XHR.status = "error";
169 | params.error(XHR, XHR.status, "");
170 | return;
171 | }
172 | // Got payload
173 | params.success(JSON.parse(XHR.responseText));
174 | }
175 | } catch (e) {
176 | // Something broke up
177 | params.error(XHR, 'error', '');
178 | };
179 | };
180 | // Detect tab close
181 | window.onbeforeunload = function () {
182 | Janus.log("Closing window");
183 | for (var s in Janus.sessions) {
184 | if (Janus.sessions[s] !== null && Janus.sessions[s] !== undefined &&
185 | Janus.sessions[s].destroyOnUnload) {
186 | Janus.log("Destroying session " + s);
187 | Janus.sessions[s].destroy();
188 | }
189 | }
190 | }
191 |
192 | Janus.initDone = true;
193 | // addJsList(["adapter.js"]); // We may need others in the future
194 | }
195 | };
196 |
197 | // Helper method to check whether WebRTC is supported by this browser
198 | Janus.isWebrtcSupported = function () {
199 | return window.RTCPeerConnection && window.getUserMedia;
200 | };
201 |
202 |
203 | // Janus session object
204 | export function Janus(gatewayCallbacks) {
205 | if (Janus.initDone === undefined) {
206 | gatewayCallbacks.error("Library not initialized");
207 | return {};
208 | }
209 | Janus.log("Library initialized: " + Janus.initDone);
210 | gatewayCallbacks = gatewayCallbacks || {};
211 | gatewayCallbacks.success = (typeof gatewayCallbacks.success == "function") ? gatewayCallbacks.success : Janus.noop;
212 | gatewayCallbacks.error = (typeof gatewayCallbacks.error == "function") ? gatewayCallbacks.error : Janus.noop;
213 | gatewayCallbacks.destroyed = (typeof gatewayCallbacks.destroyed == "function") ? gatewayCallbacks.destroyed : Janus.noop;
214 | if (gatewayCallbacks.server === null || gatewayCallbacks.server === undefined) {
215 | gatewayCallbacks.error("Invalid gateway url");
216 | return {};
217 | }
218 | var localstream = null;
219 | var websockets = false;
220 | var ws = null;
221 | var wsHandlers = {};
222 | var wsKeepaliveTimeoutId = null;
223 |
224 | var servers = null, serversIndex = 0;
225 | var server = gatewayCallbacks.server;
226 | var camera_front = gatewayCallbacks.camera_front;
227 | if (Array.isArray(server)) {
228 | Janus.log("Multiple servers provided (" + server.length + "), will use the first that works");
229 | server = null;
230 | servers = gatewayCallbacks.server;
231 | Janus.debug(servers);
232 | } else {
233 | if (server.indexOf("ws") === 0) {
234 | websockets = true;
235 | Janus.log("Using WebSockets to contact Janus: " + server);
236 | } else {
237 | websockets = false;
238 | Janus.log("Using REST API to contact Janus: " + server);
239 | }
240 | }
241 | var iceServers = gatewayCallbacks.iceServers;
242 | if (iceServers === undefined || iceServers === null)
243 | iceServers = [{ "url": "stun:stun.l.google.com:19302" }];
244 | // Whether IPv6 candidates should be gathered
245 | var ipv6Support = gatewayCallbacks.ipv6;
246 | if (ipv6Support === undefined || ipv6Support === null)
247 | ipv6Support = false;
248 | // Optional max events
249 | var maxev = null;
250 | if (gatewayCallbacks.max_poll_events !== undefined && gatewayCallbacks.max_poll_events !== null)
251 | maxev = gatewayCallbacks.max_poll_events;
252 | if (maxev < 1)
253 | maxev = 1;
254 | // Token to use (only if the token based authentication mechanism is enabled)
255 | var token = null;
256 | if (gatewayCallbacks.token !== undefined && gatewayCallbacks.token !== null)
257 | token = gatewayCallbacks.token;
258 | // API secret to use (only if the shared API secret is enabled)
259 | var apisecret = null;
260 | if (gatewayCallbacks.apisecret !== undefined && gatewayCallbacks.apisecret !== null)
261 | apisecret = gatewayCallbacks.apisecret;
262 | // Whether we should destroy this session when onbeforeunload is called
263 | this.destroyOnUnload = true;
264 | if (gatewayCallbacks.destroyOnUnload !== undefined && gatewayCallbacks.destroyOnUnload !== null)
265 | this.destroyOnUnload = (gatewayCallbacks.destroyOnUnload === true);
266 |
267 | var connected = false;
268 | var sessionId = null;
269 | var pluginHandles = {};
270 | var that = this;
271 | var retries = 0;
272 | var transactions = {};
273 | console.log("sample")
274 | createSession(gatewayCallbacks);
275 |
276 | // Public methods
277 | this.getServer = function () { return server; };
278 | this.isConnected = function () { return connected; };
279 | this.getSessionId = function () { return sessionId; };
280 | this.destroy = function (callbacks) { destroySession(callbacks); };
281 | this.attach = function (callbacks) { createHandle(callbacks); };
282 |
283 | // Private method to create random identifiers (e.g., transaction)
284 | function randomString(len) {
285 | charSet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
286 | var randomString = '';
287 | for (var i = 0; i < len; i++) {
288 | var randomPoz = Math.floor(Math.random() * charSet.length);
289 | randomString += charSet.substring(randomPoz, randomPoz + 1);
290 | }
291 | return randomString;
292 | }
293 |
294 | function eventHandler() {
295 | if (sessionId == null)
296 | return;
297 | Janus.debug('Long poll...');
298 | if (!connected) {
299 | Janus.warn("Is the gateway down? (connected=false)");
300 | return;
301 | }
302 | var longpoll = server + "/" + sessionId + "?rid=" + new Date().getTime();
303 | if (maxev !== undefined && maxev !== null)
304 | longpoll = longpoll + "&maxev=" + maxev;
305 | if (token !== null && token !== undefined)
306 | longpoll = longpoll + "&token=" + token;
307 | if (apisecret !== null && apisecret !== undefined)
308 | longpoll = longpoll + "&apisecret=" + apisecret;
309 | Janus.ajax({
310 | type: 'GET',
311 | url: longpoll,
312 | cache: false,
313 | timeout: 1000, // FIXME
314 | success: handleEvent,
315 | sucess: function (json) {
316 | console.log(json)
317 | },
318 | error: function (XMLHttpRequest, textStatus, errorThrown) {
319 | console.log(XMLHttpRequest)
320 | Janus.error(textStatus + ": " + errorThrown);
321 | //~ clearTimeout(timeoutTimer);
322 | retries++;
323 | if (retries > 1) {
324 | // Did we just lose the gateway? :-(
325 | connected = false;
326 | gatewayCallbacks.error("Lost connection to the gateway (is it down?)");
327 | return;
328 | }
329 | eventHandler();
330 | },
331 | dataType: "json"
332 | });
333 | }
334 |
335 | // Private event handler: this will trigger plugin callbacks, if set
336 | function handleEvent(json) {
337 | retries = 0;
338 | if (!websockets && sessionId !== undefined && sessionId !== null)
339 | setTimeout(eventHandler, 200);
340 | Janus.debug("Got event on session " + sessionId);
341 | Janus.debug(json);
342 | if (!websockets && Array.isArray(json)) {
343 | // We got an array: it means we passed a maxev > 1, iterate on all objects
344 | for (var i = 0; i < json.length; i++) {
345 | handleEvent(json[i]);
346 | }
347 | return;
348 | }
349 | if (json["janus"] === "keepalive") {
350 | // Nothing happened
351 | return;
352 | } else if (json["janus"] === "ack") {
353 | // Just an ack, we can probably ignore
354 | var transaction = json["transaction"];
355 | if (transaction !== null && transaction !== undefined) {
356 | var reportSuccess = transactions[transaction];
357 | if (reportSuccess !== null && reportSuccess !== undefined) {
358 | reportSuccess(json);
359 | }
360 | delete transactions[transaction];
361 | }
362 | return;
363 | } else if (json["janus"] === "success") {
364 | // Success!
365 | var transaction = json["transaction"];
366 | if (transaction !== null && transaction !== undefined) {
367 | var reportSuccess = transactions[transaction];
368 | if (reportSuccess !== null && reportSuccess !== undefined) {
369 | reportSuccess(json);
370 | }
371 | delete transactions[transaction];
372 | }
373 | return;
374 | } else if (json["janus"] === "webrtcup") {
375 | // The PeerConnection with the gateway is up! Notify this
376 | var sender = json["sender"];
377 | if (sender === undefined || sender === null) {
378 | Janus.warn("Missing sender...");
379 | return;
380 | }
381 | var pluginHandle = pluginHandles[sender];
382 | if (pluginHandle === undefined || pluginHandle === null) {
383 | Janus.warn("This handle is not attached to this session");
384 | return;
385 | }
386 | pluginHandle.webrtcState(true);
387 | return;
388 | } else if (json["janus"] === "hangup") {
389 | // A plugin asked the core to hangup a PeerConnection on one of our handles
390 | var sender = json["sender"];
391 | if (sender === undefined || sender === null) {
392 | Janus.warn("Missing sender...");
393 | return;
394 | }
395 | var pluginHandle = pluginHandles[sender];
396 | if (pluginHandle === undefined || pluginHandle === null) {
397 | Janus.warn("This handle is not attached to this session");
398 | return;
399 | }
400 | pluginHandle.webrtcState(false);
401 | pluginHandle.hangup();
402 | } else if (json["janus"] === "detached") {
403 | // A plugin asked the core to detach one of our handles
404 | var sender = json["sender"];
405 | if (sender === undefined || sender === null) {
406 | Janus.warn("Missing sender...");
407 | return;
408 | }
409 | var pluginHandle = pluginHandles[sender];
410 | if (pluginHandle === undefined || pluginHandle === null) {
411 | // Don't warn here because destroyHandle causes this situation.
412 | return;
413 | }
414 | pluginHandle.ondetached();
415 | pluginHandle.detach();
416 | } else if (json["janus"] === "media") {
417 | // Media started/stopped flowing
418 | var sender = json["sender"];
419 | if (sender === undefined || sender === null) {
420 | Janus.warn("Missing sender...");
421 | return;
422 | }
423 | var pluginHandle = pluginHandles[sender];
424 | if (pluginHandle === undefined || pluginHandle === null) {
425 | Janus.warn("This handle is not attached to this session");
426 | return;
427 | }
428 | pluginHandle.mediaState(json["type"], json["receiving"]);
429 | } else if (json["janus"] === "error") {
430 | // Oops, something wrong happened
431 | // Janus.error("Ooops: " + json["error"].code + " " + json["error"].reason); // FIXME
432 | var transaction = json["transaction"];
433 | if (transaction !== null && transaction !== undefined) {
434 | var reportSuccess = transactions[transaction];
435 | if (reportSuccess !== null && reportSuccess !== undefined) {
436 | reportSuccess(json);
437 | }
438 | delete transactions[transaction];
439 | }
440 | return;
441 | } else if (json["janus"] === "event") {
442 | var sender = json["sender"];
443 | if (sender === undefined || sender === null) {
444 | Janus.warn("Missing sender...");
445 | return;
446 | }
447 | var plugindata = json["plugindata"];
448 | if (plugindata === undefined || plugindata === null) {
449 | Janus.warn("Missing plugindata...");
450 | return;
451 | }
452 | Janus.debug(" -- Event is coming from " + sender + " (" + plugindata["plugin"] + ")");
453 | var data = plugindata["data"];
454 | Janus.debug(data);
455 | var pluginHandle = pluginHandles[sender];
456 | if (pluginHandle === undefined || pluginHandle === null) {
457 | Janus.warn("This handle is not attached to this session");
458 | return;
459 | }
460 | var jsep = json["jsep"];
461 | if (jsep !== undefined && jsep !== null) {
462 | Janus.debug("Handling SDP as well...");
463 | Janus.debug(jsep);
464 | }
465 | var callback = pluginHandle.onmessage;
466 | if (callback !== null && callback !== undefined) {
467 | Janus.debug("Notifying application...");
468 | // Send to callback specified when attaching plugin handle
469 | callback(data, jsep);
470 | } else {
471 | // Send to generic callback (?)
472 | Janus.debug("No provided notification callback");
473 | }
474 | } else {
475 | Janus.warn("Unknown message '" + json["janus"] + "'");
476 | }
477 | }
478 |
479 | // Private helper to send keep-alive messages on WebSockets
480 | function keepAlive() {
481 | if (server === null || !websockets || !connected)
482 | return;
483 | wsKeepaliveTimeoutId = setTimeout(keepAlive, 30000);
484 | var request = { "janus": "keepalive", "session_id": sessionId, "transaction": randomString(12) };
485 | if (token !== null && token !== undefined)
486 | request["token"] = token;
487 | if (apisecret !== null && apisecret !== undefined)
488 | request["apisecret"] = apisecret;
489 | ws.send(JSON.stringify(request));
490 | }
491 |
492 | // Private method to create a session
493 | function createSession(callbacks) {
494 | var transaction = randomString(12);
495 | var request = { "janus": "create", "transaction": transaction };
496 | if (token !== null && token !== undefined)
497 | request["token"] = token;
498 | if (apisecret !== null && apisecret !== undefined)
499 | request["apisecret"] = apisecret;
500 | if (server === null && Array.isArray(servers)) {
501 | // We still need to find a working server from the list we were given
502 | server = servers[serversIndex];
503 | if (server.indexOf("ws") === 0) {
504 | websockets = true;
505 | Alert.alert("Server #" + (serversIndex + 1) + ": trying WebSockets to contact Janus (" + server + ")");
506 | } else {
507 | websockets = false;
508 | Alert.alert("Server #" + (serversIndex + 1) + ": trying REST API to contact Janus (" + server + ")");
509 | }
510 | }
511 | if (websockets) {
512 | ws = new WebSocket(server, 'janus-protocol');
513 | wsHandlers = {
514 | 'error': function () {
515 | Alert.alert("Error connecting to the Janus WebSockets server... " + server);
516 | if (Array.isArray(servers)) {
517 | serversIndex++;
518 | if (serversIndex == servers.length) {
519 | // We tried all the servers the user gave us and they all failed
520 | callbacks.error("Error connecting to any of the provided Janus servers: Is the gateway down?");
521 | return;
522 | }
523 | // Let's try the next server
524 | server = null;
525 | setTimeout(function () {
526 | createSession(callbacks);
527 | }, 200);
528 | return;
529 | }
530 | callbacks.error("Error connecting to the Janus WebSockets server: Is the gateway down?");
531 | },
532 |
533 | 'open': function () {
534 | // We need to be notified about the success
535 | transactions[transaction] = function (json) {
536 | Janus.debug(json);
537 | if (json["janus"] !== "success") {
538 | Janus.error("Ooops: " + json["error"].code + " " + json["error"].reason); // FIXME
539 | callbacks.error(json["error"].reason);
540 | return;
541 | }
542 | wsKeepaliveTimeoutId = setTimeout(keepAlive, 30000);
543 | connected = true;
544 | sessionId = json.data["id"];
545 | Janus.log("Created session: " + sessionId);
546 | Janus.sessions[sessionId] = that;
547 | callbacks.success();
548 | };
549 | ws.send(JSON.stringify(request));
550 | },
551 |
552 | 'message': function (event) {
553 | handleEvent(JSON.parse(event.data));
554 | },
555 |
556 | 'close': function () {
557 | if (server === null || !connected) {
558 | return;
559 | }
560 | connected = false;
561 | // FIXME What if this is called when the page is closed?
562 | gatewayCallbacks.error("Lost connection to the gateway (is it down?)");
563 | }
564 | };
565 |
566 | for (var eventName in wsHandlers) {
567 | ws.addEventListener(eventName, wsHandlers[eventName]);
568 | }
569 |
570 | return;
571 | }
572 | console.log("Janus.ajax({})")
573 | console.log(server)
574 | console.log(JSON.stringify(request))
575 | Janus.ajax({
576 | type: 'POST',
577 | url: server,
578 | cache: false,
579 | contentType: "application/json",
580 | data: JSON.stringify(request),
581 | success: function (json) {
582 | Janus.debug(json);
583 | if (json["janus"] !== "success") {
584 | Janus.error("Ooops: " + json["error"].code + " " + json["error"].reason); // FIXME
585 | callbacks.error(json["error"].reason);
586 | return;
587 | }
588 | connected = true;
589 | sessionId = json.data["id"];
590 | Janus.log("Created session: " + sessionId);
591 | Janus.sessions[sessionId] = that;
592 | eventHandler();
593 | callbacks.success();
594 | },
595 | error: function (XMLHttpRequest, textStatus, errorThrown) {
596 | console.log("*********error***********")
597 | console.log(XMLHttpRequest)
598 | console.log(textStatus)
599 | console.log(errorThrown)
600 | // Janus.error(textStatus + ": " + errorThrown); // FIXME
601 | if (Array.isArray(servers)) {
602 | serversIndex++;
603 | if (serversIndex == servers.length) {
604 | // We tried all the servers the user gave us and they all failed
605 | callbacks.error("Error connecting to any of the provided Janus servers: Is the gateway down?");
606 | return;
607 | }
608 | // Let's try the next server
609 | server = null;
610 | setTimeout(function () { createSession(callbacks); }, 200);
611 | return;
612 | }
613 | if (errorThrown === "")
614 | callbacks.error(textStatus + ": Is the gateway down?");
615 | else
616 | callbacks.error(textStatus + ": " + errorThrown);
617 | },
618 | dataType: "json"
619 | });
620 | }
621 |
622 | // Private method to destroy a session
623 | function destroySession(callbacks, syncRequest) {
624 | syncRequest = (syncRequest === true);
625 | Janus.log("Destroying session " + sessionId + " (sync=" + syncRequest + ")");
626 | callbacks = callbacks || {};
627 | // FIXME This method triggers a success even when we fail
628 | callbacks.success = (typeof callbacks.success == "function") ? callbacks.success : Janus.noop;
629 | if (!connected) {
630 | Janus.warn("Is the gateway down? (connected=false)");
631 | callbacks.success();
632 | return;
633 | }
634 | if (sessionId === undefined || sessionId === null) {
635 | Janus.warn("No session to destroy");
636 | callbacks.success();
637 | gatewayCallbacks.destroyed();
638 | return;
639 | }
640 | delete Janus.sessions[sessionId];
641 | // Destroy all handles first
642 | for (var ph in pluginHandles) {
643 | var phv = pluginHandles[ph];
644 | Janus.log("Destroying handle " + phv.id + " (" + phv.plugin + ")");
645 | destroyHandle(phv.id, null, syncRequest);
646 | }
647 | // Ok, go on
648 | var request = { "janus": "destroy", "transaction": randomString(12) };
649 | if (token !== null && token !== undefined)
650 | request["token"] = token;
651 | if (apisecret !== null && apisecret !== undefined)
652 | request["apisecret"] = apisecret;
653 | if (websockets) {
654 | request["session_id"] = sessionId;
655 |
656 | var unbindWebSocket = function () {
657 | for (var eventName in wsHandlers) {
658 | ws.removeEventListener(eventName, wsHandlers[eventName]);
659 | }
660 | ws.removeEventListener('message', onUnbindMessage);
661 | ws.removeEventListener('error', onUnbindError);
662 | if (wsKeepaliveTimeoutId) {
663 | clearTimeout(wsKeepaliveTimeoutId);
664 | }
665 | };
666 |
667 | var onUnbindMessage = function (event) {
668 | var data = JSON.parse(event.data);
669 | if (data.session_id == request.session_id && data.transaction == request.transaction) {
670 | unbindWebSocket();
671 | callbacks.success();
672 | gatewayCallbacks.destroyed();
673 | }
674 | };
675 | var onUnbindError = function (event) {
676 | unbindWebSocket();
677 | callbacks.error("Failed to destroy the gateway: Is the gateway down?");
678 | gatewayCallbacks.destroyed();
679 | };
680 |
681 | ws.addEventListener('message', onUnbindMessage);
682 | ws.addEventListener('error', onUnbindError);
683 |
684 | ws.send(JSON.stringify(request));
685 | return;
686 | }
687 | console.log("sessionId")
688 | console.log(sessionId)
689 | console.log("server")
690 | console.log(server)
691 | Janus.ajax({
692 | type: 'POST',
693 | url: server + "/" + sessionId,
694 | async: true, // Sometimes we need false here, or destroying in onbeforeunload won't work
695 | cache: false,
696 | contentType: "application/json",
697 | data: JSON.stringify(request),
698 | success: function (json) {
699 | Janus.log("Destroyed session:");
700 | Janus.debug(json);
701 | sessionId = null;
702 | connected = false;
703 | if (json["janus"] !== "success") {
704 | Janus.error("Ooops: " + json["error"].code + " " + json["error"].reason); // FIXME
705 | }
706 | callbacks.success();
707 | gatewayCallbacks.destroyed();
708 | },
709 | error: function (XMLHttpRequest, textStatus, errorThrown) {
710 | Janus.error(textStatus + ": " + errorThrown); // FIXME
711 | // Reset everything anyway
712 | sessionId = null;
713 | connected = false;
714 | callbacks.success();
715 | gatewayCallbacks.destroyed();
716 | },
717 | dataType: "json"
718 | });
719 | }
720 |
721 | // Private method to create a plugin handle
722 | function createHandle(callbacks) {
723 | callbacks = callbacks || {};
724 | callbacks.success = (typeof callbacks.success == "function") ? callbacks.success : Janus.noop;
725 | callbacks.error = (typeof callbacks.error == "function") ? callbacks.error : Janus.noop;
726 | callbacks.consentDialog = (typeof callbacks.consentDialog == "function") ? callbacks.consentDialog : Janus.noop;
727 | callbacks.mediaState = (typeof callbacks.mediaState == "function") ? callbacks.mediaState : Janus.noop;
728 | callbacks.webrtcState = (typeof callbacks.webrtcState == "function") ? callbacks.webrtcState : Janus.noop;
729 | callbacks.onmessage = (typeof callbacks.onmessage == "function") ? callbacks.onmessage : Janus.noop;
730 | callbacks.onlocalstream = (typeof callbacks.onlocalstream == "function") ? callbacks.onlocalstream : Janus.noop;
731 | callbacks.onremotestream = (typeof callbacks.onremotestream == "function") ? callbacks.onremotestream : Janus.noop;
732 | callbacks.ondata = (typeof callbacks.ondata == "function") ? callbacks.ondata : Janus.noop;
733 | callbacks.ondataopen = (typeof callbacks.ondataopen == "function") ? callbacks.ondataopen : Janus.noop;
734 | callbacks.oncleanup = (typeof callbacks.oncleanup == "function") ? callbacks.oncleanup : Janus.noop;
735 | callbacks.ondetached = (typeof callbacks.ondetached == "function") ? callbacks.ondetached : Janus.noop;
736 | if (!connected) {
737 | Janus.warn("Is the gateway down? (connected=false)");
738 | callbacks.error("Is the gateway down? (connected=false)");
739 | return;
740 | }
741 | var plugin = callbacks.plugin;
742 | if (plugin === undefined || plugin === null) {
743 | Janus.error("Invalid plugin");
744 | callbacks.error("Invalid plugin");
745 | return;
746 | }
747 | var transaction = randomString(12);
748 | var request = { "janus": "attach", "plugin": plugin, "transaction": transaction };
749 | if (token !== null && token !== undefined)
750 | request["token"] = token;
751 | if (apisecret !== null && apisecret !== undefined)
752 | request["apisecret"] = apisecret;
753 | if (websockets) {
754 | transactions[transaction] = function (json) {
755 | Janus.debug(json);
756 | if (json["janus"] !== "success") {
757 | Janus.error("Ooops: " + json["error"].code + " " + json["error"].reason); // FIXME
758 | callbacks.error("Ooops: " + json["error"].code + " " + json["error"].reason);
759 | return;
760 | }
761 | var handleId = json.data["id"];
762 | Janus.log("Created handle: " + handleId);
763 | var pluginHandle =
764 | {
765 | session: that,
766 | plugin: plugin,
767 | id: handleId,
768 | webrtcStuff: {
769 | started: false,
770 | myStream: null,
771 | streamExternal: false,
772 | remoteStream: null,
773 | mySdp: null,
774 | pc: null,
775 | dataChannel: null,
776 | dtmfSender: null,
777 | trickle: true,
778 | iceDone: false,
779 | sdpSent: false,
780 | volume: {
781 | value: null,
782 | timer: null
783 | },
784 | bitrate: {
785 | value: null,
786 | bsnow: null,
787 | bsbefore: null,
788 | tsnow: null,
789 | tsbefore: null,
790 | timer: null
791 | }
792 | },
793 | getId: function () { return handleId; },
794 | getPlugin: function () { return plugin; },
795 | getVolume: function () { return getVolume(handleId); },
796 | isAudioMuted: function () { return isMuted(handleId, false); },
797 | muteAudio: function () { return mute(handleId, false, true); },
798 | unmuteAudio: function () { return mute(handleId, false, false); },
799 | isVideoMuted: function () { return isMuted(handleId, true); },
800 | muteVideo: function () { return mute(handleId, true, true); },
801 | unmuteVideo: function () { return mute(handleId, true, false); },
802 | getBitrate: function () { return getBitrate(handleId); },
803 | changeLocalCamera: function (isFront) { return changeLocalCamera(handleId, isFront); },
804 | send: function (callbacks) { sendMessage(handleId, callbacks); },
805 | data: function (callbacks) { sendData(handleId, callbacks); },
806 | dtmf: function (callbacks) { sendDtmf(handleId, callbacks); },
807 | consentDialog: callbacks.consentDialog,
808 | mediaState: callbacks.mediaState,
809 | webrtcState: callbacks.webrtcState,
810 | onmessage: callbacks.onmessage,
811 | createOffer: function (callbacks) { prepareWebrtc(handleId, callbacks); },
812 | createAnswer: function (callbacks) { prepareWebrtc(handleId, callbacks); },
813 | handleRemoteJsep: function (callbacks) { prepareWebrtcPeer(handleId, callbacks); },
814 | onlocalstream: callbacks.onlocalstream,
815 | onremotestream: callbacks.onremotestream,
816 | ondata: callbacks.ondata,
817 | ondataopen: callbacks.ondataopen,
818 | oncleanup: callbacks.oncleanup,
819 | ondetached: callbacks.ondetached,
820 | hangup: function (sendRequest) { cleanupWebrtc(handleId, sendRequest === true); },
821 | detach: function (callbacks) { destroyHandle(handleId, callbacks); }
822 | }
823 | pluginHandles[handleId] = pluginHandle;
824 | callbacks.success(pluginHandle);
825 | };
826 | request["session_id"] = sessionId;
827 | ws.send(JSON.stringify(request));
828 | return;
829 | }
830 | Janus.ajax({
831 | type: 'POST',
832 | url: server + "/" + sessionId,
833 | cache: false,
834 | contentType: "application/json",
835 | data: JSON.stringify(request),
836 | success: function (json) {
837 | Janus.debug(json);
838 | if (json["janus"] !== "success") {
839 | Janus.error("Ooops: " + json["error"].code + " " + json["error"].reason); // FIXME
840 | callbacks.error("Ooops: " + json["error"].code + " " + json["error"].reason);
841 | return;
842 | }
843 | var handleId = json.data["id"];
844 | Janus.log("Created handle: " + handleId);
845 | var pluginHandle =
846 | {
847 | session: that,
848 | plugin: plugin,
849 | id: handleId,
850 | webrtcStuff: {
851 | started: false,
852 | myStream: null,
853 | streamExternal: false,
854 | remoteStream: null,
855 | mySdp: null,
856 | pc: null,
857 | dataChannel: null,
858 | dtmfSender: null,
859 | trickle: true,
860 | iceDone: false,
861 | sdpSent: false,
862 | volume: {
863 | value: null,
864 | timer: null
865 | },
866 | bitrate: {
867 | value: null,
868 | bsnow: null,
869 | bsbefore: null,
870 | tsnow: null,
871 | tsbefore: null,
872 | timer: null
873 | }
874 | },
875 | getId: function () { return handleId; },
876 | getPlugin: function () { return plugin; },
877 | getVolume: function () { return getVolume(handleId); },
878 | isAudioMuted: function () { return isMuted(handleId, false); },
879 | muteAudio: function () { return mute(handleId, false, true); },
880 | unmuteAudio: function () { return mute(handleId, false, false); },
881 | isVideoMuted: function () { return isMuted(handleId, true); },
882 | muteVideo: function () { return mute(handleId, true, true); },
883 | unmuteVideo: function () { return mute(handleId, true, false); },
884 | getBitrate: function () { return getBitrate(handleId); },
885 | changeLocalCamera: function (isFront) { return changeLocalCamera(handleId, isFront); },
886 | send: function (callbacks) { sendMessage(handleId, callbacks); },
887 | data: function (callbacks) { sendData(handleId, callbacks); },
888 | dtmf: function (callbacks) { sendDtmf(handleId, callbacks); },
889 | consentDialog: callbacks.consentDialog,
890 | mediaState: callbacks.mediaState,
891 | webrtcState: callbacks.webrtcState,
892 | onmessage: callbacks.onmessage,
893 | createOffer: function (callbacks) { prepareWebrtc(handleId, callbacks); },
894 | createAnswer: function (callbacks) { prepareWebrtc(handleId, callbacks); },
895 | handleRemoteJsep: function (callbacks) { prepareWebrtcPeer(handleId, callbacks); },
896 | onlocalstream: callbacks.onlocalstream,
897 | onremotestream: callbacks.onremotestream,
898 | ondata: callbacks.ondata,
899 | ondataopen: callbacks.ondataopen,
900 | oncleanup: callbacks.oncleanup,
901 | ondetached: callbacks.ondetached,
902 | hangup: function (sendRequest) { cleanupWebrtc(handleId, sendRequest === true); },
903 | detach: function (callbacks) { destroyHandle(handleId, callbacks); }
904 | }
905 | pluginHandles[handleId] = pluginHandle;
906 | callbacks.success(pluginHandle);
907 | },
908 | error: function (XMLHttpRequest, textStatus, errorThrown) {
909 | Janus.error(textStatus + ": " + errorThrown); // FIXME
910 | },
911 | dataType: "json"
912 | });
913 | }
914 |
915 | // Private method to send a message
916 | function sendMessage(handleId, callbacks) {
917 | callbacks = callbacks || {};
918 | callbacks.success = (typeof callbacks.success == "function") ? callbacks.success : Janus.noop;
919 | callbacks.error = (typeof callbacks.error == "function") ? callbacks.error : Janus.noop;
920 | if (!connected) {
921 | Janus.warn("Is the gateway down? (connected=false)");
922 | callbacks.error("Is the gateway down? (connected=false)");
923 | return;
924 | }
925 | var message = callbacks.message;
926 | var jsep = callbacks.jsep;
927 | var transaction = randomString(12);
928 | var request = { "janus": "message", "body": message, "transaction": transaction };
929 | if (token !== null && token !== undefined)
930 | request["token"] = token;
931 | if (apisecret !== null && apisecret !== undefined)
932 | request["apisecret"] = apisecret;
933 | if (jsep !== null && jsep !== undefined)
934 | request.jsep = jsep;
935 | Janus.debug("Sending message to plugin (handle=" + handleId + "):");
936 | Janus.debug(request);
937 | if (websockets) {
938 | request["session_id"] = sessionId;
939 | request["handle_id"] = handleId;
940 | transactions[transaction] = function (json) {
941 | Janus.debug("Message sent!");
942 | Janus.debug(json);
943 | if (json["janus"] === "success") {
944 | // We got a success, must have been a synchronous transaction
945 | var plugindata = json["plugindata"];
946 | if (plugindata === undefined || plugindata === null) {
947 | Janus.warn("Request succeeded, but missing plugindata...");
948 | callbacks.success();
949 | return;
950 | }
951 | Janus.log("Synchronous transaction successful (" + plugindata["plugin"] + ")");
952 | var data = plugindata["data"];
953 | Janus.debug(data);
954 | callbacks.success(data);
955 | return;
956 | } else if (json["janus"] !== "ack") {
957 | // Not a success and not an ack, must be an error
958 | if (json["error"] !== undefined && json["error"] !== null) {
959 | Janus.error("Ooops: " + json["error"].code + " " + json["error"].reason); // FIXME
960 | callbacks.error(json["error"].code + " " + json["error"].reason);
961 | } else {
962 | Janus.error("Unknown error"); // FIXME
963 | callbacks.error("Unknown error");
964 | }
965 | return;
966 | }
967 | // If we got here, the plugin decided to handle the request asynchronously
968 | callbacks.success();
969 | };
970 | ws.send(JSON.stringify(request));
971 | return;
972 | }
973 | Janus.ajax({
974 | type: 'POST',
975 | url: server + "/" + sessionId + "/" + handleId,
976 | cache: false,
977 | contentType: "application/json",
978 | data: JSON.stringify(request),
979 | success: function (json) {
980 | Janus.debug("Message sent!");
981 | Janus.debug(json);
982 | if (json["janus"] === "success") {
983 | // We got a success, must have been a synchronous transaction
984 | var plugindata = json["plugindata"];
985 | if (plugindata === undefined || plugindata === null) {
986 | Janus.warn("Request succeeded, but missing plugindata...");
987 | callbacks.success();
988 | return;
989 | }
990 | Janus.log("Synchronous transaction successful (" + plugindata["plugin"] + ")");
991 | var data = plugindata["data"];
992 | Janus.debug(data);
993 | callbacks.success(data);
994 | return;
995 | } else if (json["janus"] !== "ack") {
996 | // Not a success and not an ack, must be an error
997 | if (json["error"] !== undefined && json["error"] !== null) {
998 | Janus.error("Ooops: " + json["error"].code + " " + json["error"].reason); // FIXME
999 | callbacks.error(json["error"].code + " " + json["error"].reason);
1000 | } else {
1001 | Janus.error("Unknown error"); // FIXME
1002 | callbacks.error("Unknown error");
1003 | }
1004 | return;
1005 | }
1006 | // If we got here, the plugin decided to handle the request asynchronously
1007 | callbacks.success();
1008 | },
1009 | error: function (XMLHttpRequest, textStatus, errorThrown) {
1010 | Janus.error(textStatus + ": " + errorThrown); // FIXME
1011 | callbacks.error(textStatus + ": " + errorThrown);
1012 | },
1013 | dataType: "json"
1014 | });
1015 | }
1016 |
1017 | // Private method to send a trickle candidate
1018 | function sendTrickleCandidate(handleId, candidate) {
1019 | if (!connected) {
1020 | Janus.warn("Is the gateway down? (connected=false)");
1021 | return;
1022 | }
1023 | var request = { "janus": "trickle", "candidate": candidate, "transaction": randomString(12) };
1024 | if (token !== null && token !== undefined)
1025 | request["token"] = token;
1026 | if (apisecret !== null && apisecret !== undefined)
1027 | request["apisecret"] = apisecret;
1028 | Janus.debug("Sending trickle candidate (handle=" + handleId + "):");
1029 | Janus.debug(request);
1030 | if (websockets) {
1031 | request["session_id"] = sessionId;
1032 | request["handle_id"] = handleId;
1033 | ws.send(JSON.stringify(request));
1034 | return;
1035 | }
1036 | Janus.ajax({
1037 | type: 'POST',
1038 | url: server + "/" + sessionId + "/" + handleId,
1039 | cache: false,
1040 | contentType: "application/json",
1041 | data: JSON.stringify(request),
1042 | success: function (json) {
1043 | Janus.debug("Candidate sent!");
1044 | Janus.debug(json);
1045 | if (json["janus"] !== "ack") {
1046 | Janus.error("Ooops: " + json["error"].code + " " + json["error"].reason); // FIXME
1047 | return;
1048 | }
1049 | },
1050 | error: function (XMLHttpRequest, textStatus, errorThrown) {
1051 | Janus.error(textStatus + ": " + errorThrown); // FIXME
1052 | },
1053 | dataType: "json"
1054 | });
1055 | }
1056 |
1057 | // Private method to send a data channel message
1058 | function sendData(handleId, callbacks) {
1059 | callbacks = callbacks || {};
1060 | callbacks.success = (typeof callbacks.success == "function") ? callbacks.success : Janus.noop;
1061 | callbacks.error = (typeof callbacks.error == "function") ? callbacks.error : Janus.noop;
1062 | var pluginHandle = pluginHandles[handleId];
1063 | if (pluginHandle === null || pluginHandle === undefined ||
1064 | pluginHandle.webrtcStuff === null || pluginHandle.webrtcStuff === undefined) {
1065 | Janus.warn("Invalid handle");
1066 | callbacks.error("Invalid handle");
1067 | return;
1068 | }
1069 | var config = pluginHandle.webrtcStuff;
1070 | var text = callbacks.text;
1071 | if (text === null || text === undefined) {
1072 | Janus.warn("Invalid text");
1073 | callbacks.error("Invalid text");
1074 | return;
1075 | }
1076 | Janus.log("Sending string on data channel: " + text);
1077 | config.dataChannel.send(text);
1078 | callbacks.success();
1079 | }
1080 |
1081 | // Private method to send a DTMF tone
1082 | function sendDtmf(handleId, callbacks) {
1083 | callbacks = callbacks || {};
1084 | callbacks.success = (typeof callbacks.success == "function") ? callbacks.success : Janus.noop;
1085 | callbacks.error = (typeof callbacks.error == "function") ? callbacks.error : Janus.noop;
1086 | var pluginHandle = pluginHandles[handleId];
1087 | if (pluginHandle === null || pluginHandle === undefined ||
1088 | pluginHandle.webrtcStuff === null || pluginHandle.webrtcStuff === undefined) {
1089 | Janus.warn("Invalid handle");
1090 | callbacks.error("Invalid handle");
1091 | return;
1092 | }
1093 | var config = pluginHandle.webrtcStuff;
1094 | if (config.dtmfSender === null || config.dtmfSender === undefined) {
1095 | // Create the DTMF sender, if possible
1096 | if (config.myStream !== undefined && config.myStream !== null) {
1097 | var tracks = config.myStream.getAudioTracks();
1098 | if (tracks !== null && tracks !== undefined && tracks.length > 0) {
1099 | var local_audio_track = tracks[0];
1100 | config.dtmfSender = config.pc.createDTMFSender(local_audio_track);
1101 | Janus.log("Created DTMF Sender");
1102 | config.dtmfSender.ontonechange = function (tone) { Janus.debug("Sent DTMF tone: " + tone.tone); };
1103 | }
1104 | }
1105 | if (config.dtmfSender === null || config.dtmfSender === undefined) {
1106 | Janus.warn("Invalid DTMF configuration");
1107 | callbacks.error("Invalid DTMF configuration");
1108 | return;
1109 | }
1110 | }
1111 | var dtmf = callbacks.dtmf;
1112 | if (dtmf === null || dtmf === undefined) {
1113 | Janus.warn("Invalid DTMF parameters");
1114 | callbacks.error("Invalid DTMF parameters");
1115 | return;
1116 | }
1117 | var tones = dtmf.tones;
1118 | if (tones === null || tones === undefined) {
1119 | Janus.warn("Invalid DTMF string");
1120 | callbacks.error("Invalid DTMF string");
1121 | return;
1122 | }
1123 | var duration = dtmf.duration;
1124 | if (duration === null || duration === undefined)
1125 | duration = 500; // We choose 500ms as the default duration for a tone
1126 | var gap = dtmf.gap;
1127 | if (gap === null || gap === undefined)
1128 | gap = 50; // We choose 50ms as the default gap between tones
1129 | Janus.debug("Sending DTMF string " + tones + " (duration " + duration + "ms, gap " + gap + "ms");
1130 | config.dtmfSender.insertDTMF(tones, duration, gap);
1131 | }
1132 |
1133 | // Private method to destroy a plugin handle
1134 | function destroyHandle(handleId, callbacks, syncRequest) {
1135 | syncRequest = false;
1136 | Janus.log("Destroying handle " + handleId + " (sync=" + syncRequest + ")");
1137 | callbacks = callbacks || {};
1138 | callbacks.success = (typeof callbacks.success == "function") ? callbacks.success : Janus.noop;
1139 | callbacks.error = (typeof callbacks.error == "function") ? callbacks.error : Janus.noop;
1140 | cleanupWebrtc(handleId);
1141 | if (!connected) {
1142 | Janus.warn("Is the gateway down? (connected=false)");
1143 | callbacks.error("Is the gateway down? (connected=false)");
1144 | return;
1145 | }
1146 | var request = { "janus": "detach", "transaction": randomString(12) };
1147 | if (token !== null && token !== undefined)
1148 | request["token"] = token;
1149 | if (apisecret !== null && apisecret !== undefined)
1150 | request["apisecret"] = apisecret;
1151 | if (websockets) {
1152 | request["session_id"] = sessionId;
1153 | request["handle_id"] = handleId;
1154 | ws.send(JSON.stringify(request));
1155 | delete pluginHandles[handleId];
1156 | callbacks.success();
1157 | return;
1158 | }
1159 | Janus.ajax({
1160 | type: 'POST',
1161 | url: server + "/" + sessionId + "/" + handleId,
1162 | async: true, // Sometimes we need false here, or destroying in onbeforeunload won't work
1163 | cache: false,
1164 | contentType: "application/json",
1165 | data: JSON.stringify(request),
1166 | success: function (json) {
1167 | Janus.log("Destroyed handle:");
1168 | Janus.debug(json);
1169 | if (json["janus"] !== "success") {
1170 | Janus.log("Ooops: " + json["error"].code + " " + json["error"].reason); // FIXME
1171 | }
1172 | delete pluginHandles[handleId];
1173 | callbacks.success();
1174 | },
1175 | error: function (XMLHttpRequest, textStatus, errorThrown) {
1176 | Janus.error(textStatus + ": " + errorThrown); // FIXME
1177 | // We cleanup anyway
1178 | delete pluginHandles[handleId];
1179 | callbacks.success();
1180 | },
1181 | dataType: "json"
1182 | });
1183 | }
1184 |
1185 |
1186 |
1187 |
1188 | // WebRTC stuff
1189 | function changeLocalCamera(handleId) {
1190 | localstream._tracks[1]._switchCamera()
1191 | }
1192 |
1193 | function streamsDone(handleId, jsep, media, callbacks, stream) {
1194 | var pluginHandle = pluginHandles[handleId];
1195 | if (pluginHandle === null || pluginHandle === undefined ||
1196 | pluginHandle.webrtcStuff === null || pluginHandle.webrtcStuff === undefined) {
1197 | Janus.warn("Invalid handle");
1198 | callbacks.error("Invalid handle");
1199 | return;
1200 | }
1201 | var config = pluginHandle.webrtcStuff;
1202 | console.log("streamsDone:", stream)
1203 | Janus.debug("streamsDone:", stream);
1204 | config.myStream = stream;
1205 | var pc_config = { "iceServers": iceServers };
1206 | //~ var pc_constraints = {'mandatory': {'MozDontOfferDataChannel':true}};
1207 | var pc_constraints = {
1208 | "optional": [{ "DtlsSrtpKeyAgreement": true }]
1209 | };
1210 | if (ipv6Support === true) {
1211 | // FIXME This is only supported in Chrome right now
1212 | // For support in Firefox track this: https://bugzilla.mozilla.org/show_bug.cgi?id=797262
1213 | pc_constraints.optional.push({ "googIPv6": true });
1214 | }
1215 | Janus.log("Creating PeerConnection");
1216 | console.log("Creating PeerConnection");
1217 | Janus.debug(pc_constraints);
1218 | config.pc = new RTCPeerConnection(pc_config, pc_constraints);
1219 | Janus.debug(config.pc);
1220 | console.log(config.pc);
1221 |
1222 | if (config.pc.getStats) { // FIXME
1223 | config.volume.value = 0;
1224 | config.bitrate.value = "0 kbits/sec";
1225 | }
1226 | Janus.log("Preparing local SDP and gathering candidates (trickle=" + config.trickle + ")");
1227 | console.log("Preparing local SDP and gathering candidates (trickle=" + config.trickle + ")");
1228 |
1229 | config.pc.onicecandidate = function (event) {
1230 | // console.log("config.pc.onicecandidate")
1231 | // if (event.candidate == null ||
1232 | // (webrtcDetectedBrowser === 'edge' && event.candidate.candidate.indexOf('endOfCandidates') > 0)) {
1233 | // Janus.log("End of candidates.");
1234 | // config.iceDone = true;
1235 | // if(config.trickle === true) {
1236 | // // Notify end of candidates
1237 | // sendTrickleCandidate(handleId, {"completed": true});
1238 | // } else {
1239 | // // No trickle, time to send the complete SDP (including all candidates)
1240 | // sendSDP(handleId, callbacks);
1241 | // }
1242 | // } else {
1243 | // JSON.stringify doesn't work on some WebRTC objects anymore
1244 | // See https://code.google.com/p/chromium/issues/detail?id=467366
1245 | if (event.candidate) {
1246 | var candidate = {
1247 | "candidate": event.candidate.candidate,
1248 | "sdpMid": event.candidate.sdpMid,
1249 | "sdpMLineIndex": event.candidate.sdpMLineIndex
1250 | };
1251 | if (config.trickle === true) {
1252 | // Send candidate
1253 | sendTrickleCandidate(handleId, candidate);
1254 | }
1255 | }
1256 | // }
1257 | };
1258 |
1259 |
1260 | if (stream !== null && stream !== undefined) {
1261 | Janus.log('Adding local stream');
1262 | console.log('Adding local stream')
1263 | config.pc.addStream(stream);
1264 | console.log('Adding local end')
1265 | pluginHandle.onlocalstream(stream);
1266 |
1267 | localstream = stream
1268 | }
1269 |
1270 | config.pc.onaddstream = function (remoteStream) {
1271 | Janus.log("Handling Remote Stream");
1272 | console.log("Handling Remote Stream");
1273 | Janus.debug(remoteStream);
1274 | config.remoteStream = remoteStream;
1275 | pluginHandle.onremotestream(remoteStream.stream);
1276 | };
1277 | // Any data channel to create?
1278 | if (isDataEnabled(media)) {
1279 | Janus.log("Creating data channel");
1280 | console.log("Creating data channel");
1281 | var onDataChannelMessage = function (event) {
1282 | Janus.log('Received message on data channel: ' + event.data);
1283 | pluginHandle.ondata(event.data); // FIXME
1284 | }
1285 | var onDataChannelStateChange = function () {
1286 | var dcState = config.dataChannel !== null ? config.dataChannel.readyState : "null";
1287 | Janus.log('State change on data channel: ' + dcState);
1288 | if (dcState === 'open') {
1289 | pluginHandle.ondataopen(); // FIXME
1290 | }
1291 | }
1292 | var onDataChannelError = function (error) {
1293 | Janus.error('Got error on data channel:', error);
1294 | // TODO
1295 | }
1296 | // Until we implement the proxying of open requests within the Janus core, we open a channel ourselves whatever the case
1297 | config.dataChannel = config.pc.createDataChannel("JanusDataChannel", { ordered: false }); // FIXME Add options (ordered, maxRetransmits, etc.)
1298 | config.dataChannel.onmessage = onDataChannelMessage;
1299 | config.dataChannel.onopen = onDataChannelStateChange;
1300 | config.dataChannel.onclose = onDataChannelStateChange;
1301 | config.dataChannel.onerror = onDataChannelError;
1302 | }
1303 | // Create offer/answer now
1304 | if (jsep === null || jsep === undefined) {
1305 | console.log("createOffer")
1306 | createOffer(handleId, media, callbacks);
1307 | } else {
1308 | config.pc.setRemoteDescription(
1309 | new RTCSessionDescription(jsep),
1310 | function () {
1311 | Janus.log("Remote description accepted!");
1312 | createAnswer(handleId, media, callbacks);
1313 | }, callbacks.error);
1314 | }
1315 | }
1316 |
1317 | function prepareWebrtc(handleId, callbacks) {
1318 |
1319 | callbacks = callbacks || {};
1320 | callbacks.success = (typeof callbacks.success == "function") ? callbacks.success : Janus.noop;
1321 | callbacks.error = (typeof callbacks.error == "function") ? callbacks.error : webrtcError;
1322 | var jsep = callbacks.jsep;
1323 | var media = callbacks.media;
1324 | var pluginHandle = pluginHandles[handleId];
1325 | if (pluginHandle === null || pluginHandle === undefined ||
1326 | pluginHandle.webrtcStuff === null || pluginHandle.webrtcStuff === undefined) {
1327 | Janus.warn("Invalid handle");
1328 | callbacks.error("Invalid handle");
1329 | return;
1330 | }
1331 | var config = pluginHandle.webrtcStuff;
1332 | // Are we updating a session?
1333 | if (config.pc !== undefined && config.pc !== null) {
1334 | Janus.log("Updating existing media session");
1335 | // Create offer/answer now
1336 | if (jsep === null || jsep === undefined) {
1337 | createOffer(handleId, media, callbacks);
1338 | } else {
1339 | config.pc.setRemoteDescription(
1340 | new RTCSessionDescription(jsep),
1341 | function () {
1342 | Janus.log("Remote description accepted!");
1343 | createAnswer(handleId, media, callbacks);
1344 | }, callbacks.error);
1345 | }
1346 | return;
1347 | }
1348 | // Was a MediaStream object passed, or do we need to take care of that?
1349 | if (callbacks.stream !== null && callbacks.stream !== undefined) {
1350 | var stream = callbacks.stream;
1351 | Janus.log("MediaStream provided by the application");
1352 | Janus.debug(stream);
1353 | // Skip the getUserMedia part
1354 | config.streamExternal = true;
1355 | streamsDone(handleId, jsep, media, callbacks, stream);
1356 | return;
1357 | }
1358 | config.trickle = isTrickleEnabled(callbacks.trickle);
1359 | if (isAudioSendEnabled(media) || isVideoSendEnabled(media)) {
1360 | var constraints = { mandatory: {}, optional: [] };
1361 | pluginHandle.consentDialog(true);
1362 | var audioSupport = isAudioSendEnabled(media);
1363 | if (audioSupport === true && media != undefined && media != null) {
1364 | if (typeof media.audio === 'object') {
1365 | audioSupport = media.audio;
1366 | }
1367 | }
1368 | var videoSupport = isVideoSendEnabled(media);
1369 | if (videoSupport === true && media != undefined && media != null) {
1370 | if (media.video && media.video != 'screen' && media.video != 'window') {
1371 | var width = 0;
1372 | var height = 0, maxHeight = 0;
1373 | if (media.video === 'lowres') {
1374 | // Small resolution, 4:3
1375 | height = 240;
1376 | maxHeight = 240;
1377 | width = 320;
1378 | } else if (media.video === 'lowres-16:9') {
1379 | // Small resolution, 16:9
1380 | height = 180;
1381 | maxHeight = 180;
1382 | width = 320;
1383 | } else if (media.video === 'hires' || media.video === 'hires-16:9') {
1384 | // High resolution is only 16:9
1385 | height = 720;
1386 | maxHeight = 720;
1387 | width = 1280;
1388 | if (navigator.mozGetUserMedia) {
1389 | var firefoxVer = parseInt(window.navigator.userAgent.match(/Firefox\/(.*)/)[1], 10);
1390 | if (firefoxVer < 38) {
1391 | // Unless this is and old Firefox, which doesn't support it
1392 | Janus.warn(media.video + " unsupported, falling back to stdres (old Firefox)");
1393 | height = 480;
1394 | maxHeight = 480;
1395 | width = 640;
1396 | }
1397 | }
1398 | } else if (media.video === 'stdres') {
1399 | // Normal resolution, 4:3
1400 | height = 480;
1401 | maxHeight = 480;
1402 | width = 640;
1403 | } else if (media.video === 'stdres-16:9') {
1404 | // Normal resolution, 16:9
1405 | height = 360;
1406 | maxHeight = 360;
1407 | width = 640;
1408 | } else {
1409 | Janus.log("Default video setting (" + media.video + ") is stdres 4:3");
1410 | height = 480;
1411 | maxHeight = 480;
1412 | width = 640;
1413 | }
1414 | Janus.log("Adding media constraint " + media.video);
1415 | if (navigator.mozGetUserMedia) {
1416 | var firefoxVer = parseInt(window.navigator.userAgent.match(/Firefox\/(.*)/)[1], 10);
1417 | if (firefoxVer < 38) {
1418 | videoSupport = {
1419 | 'require': ['height', 'width'],
1420 | 'height': { 'max': maxHeight, 'min': height },
1421 | 'width': { 'max': width, 'min': width }
1422 | };
1423 | } else {
1424 | // http://stackoverflow.com/questions/28282385/webrtc-firefox-constraints/28911694#28911694
1425 | // https://github.com/meetecho/janus-gateway/pull/246
1426 | videoSupport = {
1427 | 'height': { 'ideal': height },
1428 | 'width': { 'ideal': width }
1429 | };
1430 | }
1431 | } else {
1432 | videoSupport = {
1433 | 'mandatory': {
1434 | 'maxHeight': maxHeight,
1435 | 'minHeight': height,
1436 | 'maxWidth': width,
1437 | 'minWidth': width
1438 | },
1439 | 'optional': []
1440 | };
1441 | }
1442 | if (typeof media.video === 'object') {
1443 | videoSupport = media.video;
1444 | }
1445 | Janus.debug(videoSupport);
1446 | } else if (media.video === 'screen' || media.video === 'window') {
1447 | // Not a webcam, but screen capture
1448 | if (window.location.protocol !== 'https:') {
1449 | // Screen sharing mandates HTTPS
1450 | Janus.warn("Screen sharing only works on HTTPS, try the https:// version of this page");
1451 | pluginHandle.consentDialog(false);
1452 | callbacks.error("Screen sharing only works on HTTPS, try the https:// version of this page");
1453 | return;
1454 | }
1455 | // We're going to try and use the extension for Chrome 34+, the old approach
1456 | // for older versions of Chrome, or the experimental support in Firefox 33+
1457 | var cache = {};
1458 | function callbackUserMedia(error, stream) {
1459 | pluginHandle.consentDialog(false);
1460 | if (error) {
1461 | callbacks.error({ code: error.code, name: error.name, message: error.message });
1462 | } else {
1463 | streamsDone(handleId, jsep, media, callbacks, stream);
1464 | }
1465 | };
1466 | function getScreenMedia(constraints, gsmCallback) {
1467 | Janus.log("Adding media constraint (screen capture)");
1468 | Janus.debug(constraints);
1469 | navigator.mediaDevices.getUserMedia(constraints)
1470 | .then(function (stream) { gsmCallback(null, stream); })
1471 | .catch(function (error) { pluginHandle.consentDialog(false); gsmCallback(error); });
1472 | };
1473 |
1474 | if (window.navigator.userAgent.match('Chrome')) {
1475 | var chromever = parseInt(window.navigator.userAgent.match(/Chrome\/(.*) /)[1], 10);
1476 | var maxver = 33;
1477 | if (window.navigator.userAgent.match('Linux'))
1478 | maxver = 35; // "known" crash in chrome 34 and 35 on linux
1479 | if (chromever >= 26 && chromever <= maxver) {
1480 | // Chrome 26->33 requires some awkward chrome://flags manipulation
1481 | constraints = {
1482 | video: {
1483 | mandatory: {
1484 | googLeakyBucket: true,
1485 | maxWidth: window.screen.width,
1486 | maxHeight: window.screen.height,
1487 | maxFrameRate: 3,
1488 | chromeMediaSource: 'screen'
1489 | }
1490 | },
1491 | audio: isAudioSendEnabled(media)
1492 | };
1493 | getScreenMedia(constraints, callbackUserMedia);
1494 | } else {
1495 | // Chrome 34+ requires an extension
1496 | var pending = window.setTimeout(
1497 | function () {
1498 | error = new Error('NavigatorUserMediaError');
1499 | error.name = 'The required Chrome extension is not installed: click here to install it. (NOTE: this will need you to refresh the page)';
1500 | pluginHandle.consentDialog(false);
1501 | return callbacks.error(error);
1502 | }, 1000);
1503 | cache[pending] = [callbackUserMedia, null];
1504 | window.postMessage({ type: 'janusGetScreen', id: pending }, '*');
1505 | }
1506 | } else if (window.navigator.userAgent.match('Firefox')) {
1507 | var ffver = parseInt(window.navigator.userAgent.match(/Firefox\/(.*)/)[1], 10);
1508 | if (ffver >= 33) {
1509 | // Firefox 33+ has experimental support for screen sharing
1510 | constraints = {
1511 | video: {
1512 | mozMediaSource: media.video,
1513 | mediaSource: media.video
1514 | },
1515 | audio: isAudioSendEnabled(media)
1516 | };
1517 | getScreenMedia(constraints, function (err, stream) {
1518 | callbackUserMedia(err, stream);
1519 | // Workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=1045810
1520 | if (!err) {
1521 | var lastTime = stream.currentTime;
1522 | var polly = window.setInterval(function () {
1523 | if (!stream)
1524 | window.clearInterval(polly);
1525 | if (stream.currentTime == lastTime) {
1526 | window.clearInterval(polly);
1527 | if (stream.onended) {
1528 | stream.onended();
1529 | }
1530 | }
1531 | lastTime = stream.currentTime;
1532 | }, 500);
1533 | }
1534 | });
1535 | } else {
1536 | var error = new Error('NavigatorUserMediaError');
1537 | error.name = 'Your version of Firefox does not support screen sharing, please install Firefox 33 (or more recent versions)';
1538 | pluginHandle.consentDialog(false);
1539 | callbacks.error(error);
1540 | return;
1541 | }
1542 | }
1543 |
1544 | // Wait for events from the Chrome Extension
1545 | window.addEventListener('message', function (event) {
1546 | if (event.origin != window.location.origin)
1547 | return;
1548 | if (event.data.type == 'janusGotScreen' && cache[event.data.id]) {
1549 | var data = cache[event.data.id];
1550 | var callback = data[0];
1551 | delete cache[event.data.id];
1552 |
1553 | if (event.data.sourceId === '') {
1554 | // user canceled
1555 | var error = new Error('NavigatorUserMediaError');
1556 | error.name = 'You cancelled the request for permission, giving up...';
1557 | pluginHandle.consentDialog(false);
1558 | callbacks.error(error);
1559 | } else {
1560 | constraints = {
1561 | audio: isAudioSendEnabled(media),
1562 | video: {
1563 | mandatory: {
1564 | chromeMediaSource: 'desktop',
1565 | maxWidth: window.screen.width,
1566 | maxHeight: window.screen.height,
1567 | maxFrameRate: 3
1568 | },
1569 | optional: [
1570 | { googLeakyBucket: true },
1571 | { googTemporalLayeredScreencast: true }
1572 | ]
1573 | }
1574 | };
1575 | constraints.video.mandatory.chromeMediaSourceId = event.data.sourceId;
1576 | getScreenMedia(constraints, callback);
1577 | }
1578 | } else if (event.data.type == 'janusGetScreenPending') {
1579 | window.clearTimeout(event.data.id);
1580 | }
1581 | });
1582 | return;
1583 | }
1584 | }
1585 | // If we got here, we're not screensharing
1586 | if (media === null || media === undefined || media.video !== 'screen') {
1587 | // Check whether all media sources are actually available or not
1588 |
1589 | mediaDevices.enumerateDevices().then(devices => {
1590 | console.log(devices);
1591 | mediaDevices.getUserMedia({
1592 | audio: true,
1593 | video: {
1594 | facingMode: (false ? "user" : "environment"),
1595 | }
1596 | }).then(stream => {
1597 | localStream = stream;
1598 | console.log("Succeeded to get the local camera!");
1599 | streamsDone(handleId, jsep, media, callbacks, stream);
1600 | }).catch(error => {
1601 | console.log("Failed to get the local camera!");
1602 | console.log(error);
1603 | })
1604 | })
1605 | }
1606 |
1607 | } else {
1608 | // No need to do a getUserMedia, create offer/answer right away
1609 | streamsDone(handleId, jsep, media, callbacks);
1610 | }
1611 | }
1612 |
1613 | function prepareWebrtcPeer(handleId, callbacks) {
1614 | callbacks = callbacks || {};
1615 | callbacks.success = (typeof callbacks.success == "function") ? callbacks.success : Janus.noop;
1616 | callbacks.error = (typeof callbacks.error == "function") ? callbacks.error : webrtcError;
1617 | var jsep = callbacks.jsep;
1618 | var pluginHandle = pluginHandles[handleId];
1619 | if (pluginHandle === null || pluginHandle === undefined ||
1620 | pluginHandle.webrtcStuff === null || pluginHandle.webrtcStuff === undefined) {
1621 | Janus.warn("Invalid handle");
1622 | callbacks.error("Invalid handle");
1623 | return;
1624 | }
1625 | var config = pluginHandle.webrtcStuff;
1626 | if (jsep !== undefined && jsep !== null) {
1627 | if (config.pc === null) {
1628 | Janus.warn("Wait, no PeerConnection?? if this is an answer, use createAnswer and not handleRemoteJsep");
1629 | callbacks.error("No PeerConnection: if this is an answer, use createAnswer and not handleRemoteJsep");
1630 | return;
1631 | }
1632 | config.pc.setRemoteDescription(
1633 | new RTCSessionDescription(jsep),
1634 | function () {
1635 | Janus.log("Remote description accepted!");
1636 | callbacks.success();
1637 | }, callbacks.error);
1638 | } else {
1639 | callbacks.error("Invalid JSEP");
1640 | }
1641 | }
1642 |
1643 | function createOffer(handleId, media, callbacks) {
1644 | callbacks = callbacks || {};
1645 | callbacks.success = (typeof callbacks.success == "function") ? callbacks.success : Janus.noop;
1646 | callbacks.error = (typeof callbacks.error == "function") ? callbacks.error : Janus.noop;
1647 | var pluginHandle = pluginHandles[handleId];
1648 | if (pluginHandle === null || pluginHandle === undefined ||
1649 | pluginHandle.webrtcStuff === null || pluginHandle.webrtcStuff === undefined) {
1650 | Janus.warn("Invalid handle");
1651 | callbacks.error("Invalid handle");
1652 | return;
1653 | }
1654 | var config = pluginHandle.webrtcStuff;
1655 | Janus.log("Creating offer (iceDone=" + config.iceDone + ")");
1656 | // https://code.google.com/p/webrtc/issues/detail?id=3508
1657 | var mediaConstraints = null;
1658 | mediaConstraints = {
1659 | 'mandatory': {
1660 | 'OfferToReceiveAudio': isAudioRecvEnabled(media),
1661 | 'OfferToReceiveVideo': isVideoRecvEnabled(media)
1662 | }
1663 | };
1664 | // }
1665 | Janus.debug(mediaConstraints);
1666 | config.pc.createOffer().then(
1667 | function (offer) {
1668 |
1669 | Janus.debug(offer);
1670 | if (config.mySdp === null || config.mySdp === undefined) {
1671 | console.log("Setting local description");
1672 | config.mySdp = offer.sdp;
1673 | // config.pc.setLocalDescription(offer);
1674 |
1675 | config.pc.setLocalDescription(offer, () => {
1676 | console.log('setLocalDescription', config.pc.localDescription);
1677 | }, function (e) { });
1678 | }
1679 | if (!config.iceDone && !config.trickle) {
1680 | // Don't do anything until we have all candidates
1681 | Janus.log("Waiting for all candidates...");
1682 | return;
1683 | }
1684 | if (config.sdpSent) {
1685 | Janus.log("Offer already sent, not sending it again");
1686 | return;
1687 | }
1688 | Janus.log("Offer ready");
1689 | Janus.debug(callbacks);
1690 | config.sdpSent = true;
1691 | // JSON.stringify doesn't work on some WebRTC objects anymore
1692 | // See https://code.google.com/p/chromium/issues/detail?id=467366
1693 | var jsep = {
1694 | "type": offer.type,
1695 | "sdp": offer.sdp
1696 | };
1697 | callbacks.success(jsep);
1698 | }).then(function (e) { });
1699 | }
1700 |
1701 | function createAnswer(handleId, media, callbacks) {
1702 | callbacks = callbacks || {};
1703 | callbacks.success = (typeof callbacks.success == "function") ? callbacks.success : Janus.noop;
1704 | callbacks.error = (typeof callbacks.error == "function") ? callbacks.error : Janus.noop;
1705 | var pluginHandle = pluginHandles[handleId];
1706 | if (pluginHandle === null || pluginHandle === undefined ||
1707 | pluginHandle.webrtcStuff === null || pluginHandle.webrtcStuff === undefined) {
1708 | Janus.warn("Invalid handle");
1709 | callbacks.error("Invalid handle");
1710 | return;
1711 | }
1712 | var config = pluginHandle.webrtcStuff;
1713 | Janus.log("Creating answer (iceDone=" + config.iceDone + ")");
1714 | var mediaConstraints = null;
1715 | // if(webrtcDetectedBrowser == "firefox" || webrtcDetectedBrowser == "edge") {
1716 | // mediaConstraints = {
1717 | // 'offerToReceiveAudio':isAudioRecvEnabled(media),
1718 | // 'offerToReceiveVideo':isVideoRecvEnabled(media)
1719 | // };
1720 | // } else {
1721 | mediaConstraints = {
1722 | 'mandatory': {
1723 | 'OfferToReceiveAudio': isAudioRecvEnabled(media),
1724 | 'OfferToReceiveVideo': isVideoRecvEnabled(media)
1725 | }
1726 | };
1727 | // }
1728 | Janus.debug(mediaConstraints);
1729 | config.pc.createAnswer(
1730 | function (answer) {
1731 | Janus.debug(answer);
1732 | if (config.mySdp === null || config.mySdp === undefined) {
1733 | Janus.log("Setting local description");
1734 | config.mySdp = answer.sdp;
1735 | config.pc.setLocalDescription(answer, () => {
1736 | // console.log('setLocalDescription', config.pc.localDescription);
1737 | }, (error) => {
1738 | console.log(error);
1739 | },
1740 | );
1741 | }
1742 | if (!config.iceDone && !config.trickle) {
1743 | // Don't do anything until we have all candidates
1744 | Janus.log("Waiting for all candidates...");
1745 | return;
1746 | }
1747 | if (config.sdpSent) { // FIXME badly
1748 | Janus.log("Answer already sent, not sending it again");
1749 | return;
1750 | }
1751 | config.sdpSent = true;
1752 | // JSON.stringify doesn't work on some WebRTC objects anymore
1753 | // See https://code.google.com/p/chromium/issues/detail?id=467366
1754 | var jsep = {
1755 | "type": answer.type,
1756 | "sdp": answer.sdp
1757 | };
1758 | callbacks.success(jsep);
1759 | }, callbacks.error, mediaConstraints);
1760 | }
1761 |
1762 | function sendSDP(handleId, callbacks) {
1763 | callbacks = callbacks || {};
1764 | callbacks.success = (typeof callbacks.success == "function") ? callbacks.success : Janus.noop;
1765 | callbacks.error = (typeof callbacks.error == "function") ? callbacks.error : Janus.noop;
1766 | var pluginHandle = pluginHandles[handleId];
1767 | if (pluginHandle === null || pluginHandle === undefined ||
1768 | pluginHandle.webrtcStuff === null || pluginHandle.webrtcStuff === undefined) {
1769 | Janus.warn("Invalid handle, not sending anything");
1770 | return;
1771 | }
1772 | var config = pluginHandle.webrtcStuff;
1773 | Janus.log("Sending offer/answer SDP...");
1774 | if (config.mySdp === null || config.mySdp === undefined) {
1775 | Janus.warn("Local SDP instance is invalid, not sending anything...");
1776 | return;
1777 | }
1778 | config.mySdp = {
1779 | "type": config.pc.localDescription.type,
1780 | "sdp": config.pc.localDescription.sdp
1781 | };
1782 | if (config.sdpSent) {
1783 | Janus.log("Offer/Answer SDP already sent, not sending it again");
1784 | return;
1785 | }
1786 | if (config.trickle === false)
1787 | config.mySdp["trickle"] = false;
1788 | Janus.debug(callbacks);
1789 | config.sdpSent = true;
1790 | callbacks.success(config.mySdp);
1791 | }
1792 |
1793 | function getVolume(handleId) {
1794 | var pluginHandle = pluginHandles[handleId];
1795 | if (pluginHandle === null || pluginHandle === undefined ||
1796 | pluginHandle.webrtcStuff === null || pluginHandle.webrtcStuff === undefined) {
1797 | Janus.warn("Invalid handle");
1798 | return 0;
1799 | }
1800 | var config = pluginHandle.webrtcStuff;
1801 | // Start getting the volume, if getStats is supported
1802 | if (config.pc.getStats && webrtcDetectedBrowser == "chrome") { // FIXME
1803 | if (config.remoteStream === null || config.remoteStream === undefined) {
1804 | Janus.warn("Remote stream unavailable");
1805 | return 0;
1806 | }
1807 | // http://webrtc.googlecode.com/svn/trunk/samples/js/demos/html/constraints-and-stats.html
1808 | if (config.volume.timer === null || config.volume.timer === undefined) {
1809 | Janus.log("Starting volume monitor");
1810 | config.volume.timer = setInterval(function () {
1811 | config.pc.getStats(function (stats) {
1812 | var results = stats.result();
1813 | for (var i = 0; i < results.length; i++) {
1814 | var res = results[i];
1815 | if (res.type == 'ssrc' && res.stat('audioOutputLevel')) {
1816 | config.volume.value = res.stat('audioOutputLevel');
1817 | }
1818 | }
1819 | });
1820 | }, 200);
1821 | return 0; // We don't have a volume to return yet
1822 | }
1823 | return config.volume.value;
1824 | } else {
1825 | Janus.log("Getting the remote volume unsupported by browser");
1826 | return 0;
1827 | }
1828 | }
1829 |
1830 | function isMuted(handleId, video) {
1831 | var pluginHandle = pluginHandles[handleId];
1832 | if (pluginHandle === null || pluginHandle === undefined ||
1833 | pluginHandle.webrtcStuff === null || pluginHandle.webrtcStuff === undefined) {
1834 | Janus.warn("Invalid handle");
1835 | return true;
1836 | }
1837 | var config = pluginHandle.webrtcStuff;
1838 | if (config.pc === null || config.pc === undefined) {
1839 | Janus.warn("Invalid PeerConnection");
1840 | return true;
1841 | }
1842 | if (config.myStream === undefined || config.myStream === null) {
1843 | Janus.warn("Invalid local MediaStream");
1844 | return true;
1845 | }
1846 | if (video) {
1847 | // Check video track
1848 | if (config.myStream.getVideoTracks() === null
1849 | || config.myStream.getVideoTracks() === undefined
1850 | || config.myStream.getVideoTracks().length === 0) {
1851 | Janus.warn("No video track");
1852 | return true;
1853 | }
1854 | return !config.myStream.getVideoTracks()[0].enabled;
1855 | } else {
1856 | // Check audio track
1857 | if (config.myStream.getAudioTracks() === null
1858 | || config.myStream.getAudioTracks() === undefined
1859 | || config.myStream.getAudioTracks().length === 0) {
1860 | Janus.warn("No audio track");
1861 | return true;
1862 | }
1863 | return !config.myStream.getAudioTracks()[0].enabled;
1864 | }
1865 | }
1866 |
1867 | function mute(handleId, video, mute) {
1868 | var pluginHandle = pluginHandles[handleId];
1869 | if (pluginHandle === null || pluginHandle === undefined ||
1870 | pluginHandle.webrtcStuff === null || pluginHandle.webrtcStuff === undefined) {
1871 | Janus.warn("Invalid handle");
1872 | return false;
1873 | }
1874 | var config = pluginHandle.webrtcStuff;
1875 | if (config.pc === null || config.pc === undefined) {
1876 | Janus.warn("Invalid PeerConnection");
1877 | return false;
1878 | }
1879 | if (config.myStream === undefined || config.myStream === null) {
1880 | Janus.warn("Invalid local MediaStream");
1881 | return false;
1882 | }
1883 | if (video) {
1884 | // Mute/unmute video track
1885 | if (config.myStream.getVideoTracks() === null
1886 | || config.myStream.getVideoTracks() === undefined
1887 | || config.myStream.getVideoTracks().length === 0) {
1888 | Janus.warn("No video track");
1889 | return false;
1890 | }
1891 | config.myStream.getVideoTracks()[0].enabled = mute ? false : true;
1892 | return true;
1893 | } else {
1894 | // Mute/unmute audio track
1895 | if (config.myStream.getAudioTracks() === null
1896 | || config.myStream.getAudioTracks() === undefined
1897 | || config.myStream.getAudioTracks().length === 0) {
1898 | Janus.warn("No audio track");
1899 | return false;
1900 | }
1901 | config.myStream.getAudioTracks()[0].enabled = mute ? false : true;
1902 | return true;
1903 | }
1904 | }
1905 |
1906 | function getBitrate(handleId) {
1907 | var pluginHandle = pluginHandles[handleId];
1908 | if (pluginHandle === null || pluginHandle === undefined ||
1909 | pluginHandle.webrtcStuff === null || pluginHandle.webrtcStuff === undefined) {
1910 | Janus.warn("Invalid handle");
1911 | return "Invalid handle";
1912 | }
1913 | var config = pluginHandle.webrtcStuff;
1914 | if (config.pc === null || config.pc === undefined)
1915 | return "Invalid PeerConnection";
1916 | // Start getting the bitrate, if getStats is supported
1917 | if (true) {
1918 | // Do it the Chrome way
1919 | if (config.remoteStream === null || config.remoteStream === undefined) {
1920 | Janus.warn("Remote stream unavailable");
1921 | return "Remote stream unavailable";
1922 | }
1923 | // http://webrtc.googlecode.com/svn/trunk/samples/js/demos/html/constraints-and-stats.html
1924 | if (config.bitrate.timer === null || config.bitrate.timer === undefined) {
1925 | Janus.log("Starting bitrate timer (Chrome)");
1926 | config.bitrate.timer = setInterval(function () {
1927 | config.pc.getStats(function (stats) {
1928 | var results = stats.result();
1929 | for (var i = 0; i < results.length; i++) {
1930 | var res = results[i];
1931 | if (res.type == 'ssrc' && res.stat('googFrameHeightReceived')) {
1932 | config.bitrate.bsnow = res.stat('bytesReceived');
1933 | config.bitrate.tsnow = res.timestamp;
1934 | if (config.bitrate.bsbefore === null || config.bitrate.tsbefore === null) {
1935 | // Skip this round
1936 | config.bitrate.bsbefore = config.bitrate.bsnow;
1937 | config.bitrate.tsbefore = config.bitrate.tsnow;
1938 | } else {
1939 | // Calculate bitrate
1940 | var bitRate = Math.round((config.bitrate.bsnow - config.bitrate.bsbefore) * 8 / (config.bitrate.tsnow - config.bitrate.tsbefore));
1941 | config.bitrate.value = bitRate + ' kbits/sec';
1942 | //~ Janus.log("Estimated bitrate is " + config.bitrate.value);
1943 | config.bitrate.bsbefore = config.bitrate.bsnow;
1944 | config.bitrate.tsbefore = config.bitrate.tsnow;
1945 | }
1946 | }
1947 | }
1948 | });
1949 | }, 1000);
1950 | return "0 kbits/sec"; // We don't have a bitrate value yet
1951 | }
1952 | return config.bitrate.value;
1953 | }
1954 | }
1955 |
1956 | function webrtcError(error) {
1957 | Janus.error("WebRTC error:", error);
1958 | }
1959 |
1960 | function cleanupWebrtc(handleId, hangupRequest) {
1961 |
1962 |
1963 | Janus.log("Cleaning WebRTC stuff");
1964 | console.log(handleId)
1965 | console.log(hangupRequest)
1966 | var pluginHandle = pluginHandles[handleId];
1967 | if (pluginHandle === null || pluginHandle === undefined) {
1968 | // Nothing to clean
1969 | return;
1970 | }
1971 | var config = pluginHandle.webrtcStuff;
1972 | if (config !== null && config !== undefined) {
1973 | if (hangupRequest === true) {
1974 | // Send a hangup request (we don't really care about the response)
1975 | var request = { "janus": "hangup", "transaction": randomString(12) };
1976 | if (token !== null && token !== undefined)
1977 | request["token"] = token;
1978 | if (apisecret !== null && apisecret !== undefined)
1979 | request["apisecret"] = apisecret;
1980 | Janus.debug("Sending hangup request (handle=" + handleId + "):");
1981 | Janus.debug(request);
1982 | if (websockets) {
1983 | request["session_id"] = sessionId;
1984 | request["handle_id"] = handleId;
1985 | ws.send(JSON.stringify(request));
1986 | } else {
1987 | $.ajax({
1988 | type: 'POST',
1989 | url: server + "/" + sessionId + "/" + handleId,
1990 | cache: false,
1991 | contentType: "application/json",
1992 | data: JSON.stringify(request),
1993 | dataType: "json"
1994 | });
1995 | }
1996 | }
1997 | // Cleanup stack
1998 | config.remoteStream = null;
1999 | if (config.volume.timer)
2000 | clearInterval(config.volume.timer);
2001 | config.volume.value = null;
2002 | if (config.bitrate.timer)
2003 | clearInterval(config.bitrate.timer);
2004 | config.bitrate.timer = null;
2005 | config.bitrate.bsnow = null;
2006 | config.bitrate.bsbefore = null;
2007 | config.bitrate.tsnow = null;
2008 | config.bitrate.tsbefore = null;
2009 | config.bitrate.value = null;
2010 | try {
2011 | // Try a MediaStream.stop() first
2012 | if (!config.streamExternal && config.myStream !== null && config.myStream !== undefined) {
2013 | // console.log(config)
2014 | Janus.log("Stopping local stream");
2015 | // config.pc.removeStream(localstream)
2016 | // localstream.release()
2017 | // localstream.stop()
2018 | localStream.getTracks().forEach((t) => {
2019 | localStream.removeTrack(t);
2020 | });
2021 | localStream.release();
2022 | // config.myStream.stop();
2023 | config.myStream.getTracks().forEach(function (track) { track.stop(); });
2024 | }
2025 | } catch (e) {
2026 | console.log(e)
2027 | // Do nothing if this fails
2028 | }
2029 | try {
2030 | // Try a MediaStreamTrack.stop() for each track as well
2031 | if (!config.streamExternal && config.myStream !== null && config.myStream !== undefined) {
2032 | Janus.log("Stopping local stream tracks");
2033 | var tracks = config.myStream.getTracks();
2034 | for (var i in tracks) {
2035 | var mst = tracks[i];
2036 | Janus.log(mst);
2037 | if (mst !== null && mst !== undefined)
2038 | mst.stop();
2039 | }
2040 | }
2041 | } catch (e) {
2042 | console.log(e)
2043 | // Do nothing if this fails
2044 | }
2045 | config.streamExternal = false;
2046 | config.myStream = null;
2047 | // Close PeerConnection
2048 | try {
2049 | config.pc.close();
2050 | } catch (e) {
2051 | // Do nothing
2052 | }
2053 | config.pc = null;
2054 | config.mySdp = null;
2055 | config.iceDone = false;
2056 | config.sdpSent = false;
2057 | config.dataChannel = null;
2058 | config.dtmfSender = null;
2059 | }
2060 | pluginHandle.oncleanup();
2061 | }
2062 |
2063 | // Helper methods to parse a media object
2064 | function isAudioSendEnabled(media) {
2065 | Janus.debug("isAudioSendEnabled:", media);
2066 | if (media === undefined || media === null)
2067 | return true; // Default
2068 | if (media.audio === false)
2069 | return false; // Generic audio has precedence
2070 | if (media.audioSend === undefined || media.audioSend === null)
2071 | return true; // Default
2072 | return (media.audioSend === true);
2073 | }
2074 |
2075 | function isAudioRecvEnabled(media) {
2076 | Janus.debug("isAudioRecvEnabled:", media);
2077 | if (media === undefined || media === null)
2078 | return true; // Default
2079 | if (media.audio === false)
2080 | return false; // Generic audio has precedence
2081 | if (media.audioRecv === undefined || media.audioRecv === null)
2082 | return true; // Default
2083 | return (media.audioRecv === true);
2084 | }
2085 |
2086 | function isVideoSendEnabled(media) {
2087 | Janus.debug("isVideoSendEnabled:", media);
2088 | // if(webrtcDetectedBrowser == "edge") {
2089 | // Janus.warn("Edge doesn't support compatible video yet");
2090 | // return false;
2091 | // }
2092 | if (media === undefined || media === null)
2093 | return true; // Default
2094 | if (media.video === false)
2095 | return false; // Generic video has precedence
2096 | if (media.videoSend === undefined || media.videoSend === null)
2097 | return true; // Default
2098 | return (media.videoSend === true);
2099 | }
2100 |
2101 | function isVideoRecvEnabled(media) {
2102 | Janus.debug("isVideoRecvEnabled:", media);
2103 | // if(webrtcDetectedBrowser == "edge") {
2104 | // Janus.warn("Edge doesn't support compatible video yet");
2105 | // return false;
2106 | // }
2107 | if (media === undefined || media === null)
2108 | return true; // Default
2109 | if (media.video === false)
2110 | return false; // Generic video has precedence
2111 | if (media.videoRecv === undefined || media.videoRecv === null)
2112 | return true; // Default
2113 | return (media.videoRecv === true);
2114 | }
2115 |
2116 | function isDataEnabled(media) {
2117 | Janus.debug("isDataEnabled:", media);
2118 | // if(webrtcDetectedBrowser == "edge") {
2119 | // Janus.warn("Edge doesn't support data channels yet");
2120 | // return false;
2121 | // }
2122 | if (media === undefined || media === null)
2123 | return false; // Default
2124 | return (media.data === true);
2125 | }
2126 |
2127 | function isTrickleEnabled(trickle) {
2128 | Janus.debug("isTrickleEnabled:", trickle);
2129 | if (trickle === undefined || trickle === null)
2130 | return true; // Default is true
2131 | return (trickle === true);
2132 | }
2133 | };
2134 |
2135 |
--------------------------------------------------------------------------------
/metro.config.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Metro configuration for React Native
3 | * https://github.com/facebook/react-native
4 | *
5 | * @format
6 | */
7 |
8 | module.exports = {
9 | transformer: {
10 | getTransformOptions: async () => ({
11 | transform: {
12 | experimentalImportSupport: false,
13 | inlineRequires: false,
14 | },
15 | }),
16 | },
17 | };
18 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "myApp",
3 | "version": "0.0.1",
4 | "private": true,
5 | "scripts": {
6 | "start": "react-native start",
7 | "test": "jest",
8 | "lint": "eslint ."
9 | },
10 | "dependencies": {
11 | "react": "16.8.6",
12 | "react-native": "0.60.5",
13 | "react-native-webrtc": "^1.75.0"
14 | },
15 | "devDependencies": {
16 | "@babel/core": "7.5.5",
17 | "@babel/runtime": "7.5.5",
18 | "@react-native-community/eslint-config": "0.0.3",
19 | "babel-jest": "24.9.0",
20 | "eslint": "6.2.0",
21 | "jest": "24.9.0",
22 | "metro-react-native-babel-preset": "0.54.1",
23 | "react-test-renderer": "16.8.6"
24 | },
25 | "jest": {
26 | "preset": "react-native"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------