├── .gitignore
├── .idea
├── compiler.xml
├── copyright
│ └── profiles_settings.xml
├── gradle.xml
├── misc.xml
├── modules.xml
└── runConfigurations.xml
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── sergiopaniegoblanco
│ │ └── webrtcexampleapp
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── sergiopaniegoblanco
│ │ │ └── webrtcexampleapp
│ │ │ ├── CustomPeerConnectionObserver.java
│ │ │ ├── CustomSdpObserver.java
│ │ │ ├── CustomWebSocketListener.java
│ │ │ └── MainActivity.java
│ └── res
│ │ ├── layout
│ │ └── activity_main.xml
│ │ ├── 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
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── com
│ └── sergiopaniegoblanco
│ └── webrtcexampleapp
│ └── ExampleUnitTest.java
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── index.js
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 | .externalNativeBuild
10 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
17 |
18 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 | 1.8
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # WebRTCAndroidExample
2 | This is the simpliest example of an app that uses WebRTC to send video I could made. I followed some tutorials I found on the Internet,
3 | specially [this series of posts](https://vivekc.xyz/getting-started-with-webrtc-for-android-daab1e268ff4) and Google's official [Codelab](https://codelabs.developers.google.com/codelabs/webrtc-web/#0).
4 |
5 | I wrote a brief post on the issue that you can check out [here](https://medium.com/@SergioPaniego/tutorial-on-how-to-make-the-simplest-webrtc-android-app-daacb5c8d133).
6 |
7 | I'm using WebSocket protocol to communicate with the server.
8 |
9 | ## How to run it
10 | First install the Web Socket module
11 | ```
12 | npm install websocket
13 | ```
14 | Having the module install, you only need to have nodeJS installed in your computer to run the server part that will work as signaling service. To start the server just type the following command
15 |
16 | ```
17 | node index.js
18 | ```
19 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 26
5 | buildToolsVersion '26.0.2'
6 | defaultConfig {
7 | applicationId "com.sergiopaniegoblanco.webrtcexampleapp"
8 | minSdkVersion 21
9 | targetSdkVersion 26
10 | versionCode 1
11 | versionName "1.0"
12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
13 | }
14 | buildTypes {
15 | release {
16 | minifyEnabled false
17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 | }
21 |
22 | dependencies {
23 | compile fileTree(dir: 'libs', include: ['*.jar'])
24 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
25 | exclude group: 'com.android.support', module: 'support-annotations'
26 | })
27 | compile 'com.android.support:appcompat-v7:26.+'
28 | compile 'com.android.support.constraint:constraint-layout:1.0.2'
29 | compile 'fi.vtt.nubomedia:kurento-room-client-android:1.1.2'
30 | testCompile 'junit:junit:4.12'
31 | compile 'org.whispersystems:webrtc-android:M59'
32 | compile 'com.jakewharton:butterknife:8.8.1'
33 | annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
34 | // compile "org.java-websocket:Java-WebSocket:1.3.0"
35 | compile 'com.squareup.okhttp3:okhttp:3.9.0'
36 |
37 | }
38 |
39 |
--------------------------------------------------------------------------------
/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 /Users/sergiopaniegoblanco/Library/Android/sdk/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 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
19 | # Uncomment this to preserve the line number information for
20 | # debugging stack traces.
21 | #-keepattributes SourceFile,LineNumberTable
22 |
23 | # If you keep the line number information, uncomment this to
24 | # hide the original source file name.
25 | #-renamesourcefileattribute SourceFile
26 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/sergiopaniegoblanco/webrtcexampleapp/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.sergiopaniegoblanco.webrtcexampleapp;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumentation test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() throws Exception {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.sergiopaniegoblanco.webrtcexampleapp", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/app/src/main/java/com/sergiopaniegoblanco/webrtcexampleapp/CustomPeerConnectionObserver.java:
--------------------------------------------------------------------------------
1 | package com.sergiopaniegoblanco.webrtcexampleapp;
2 |
3 | import android.util.Log;
4 |
5 | import org.webrtc.DataChannel;
6 | import org.webrtc.IceCandidate;
7 | import org.webrtc.MediaStream;
8 | import org.webrtc.PeerConnection;
9 | import org.webrtc.RtpReceiver;
10 |
11 | /**
12 | * Created by sergiopaniegoblanco on 28/09/2017.
13 | */
14 |
15 | public class CustomPeerConnectionObserver implements PeerConnection.Observer {
16 |
17 | private String logTag = this.getClass().getCanonicalName();
18 |
19 | CustomPeerConnectionObserver(String logTag) {
20 | this.logTag = this.logTag+" "+logTag;
21 | }
22 |
23 | @Override
24 | public void onSignalingChange(PeerConnection.SignalingState signalingState) {
25 | Log.d(logTag, "onSignalingChange() called with: signalingState = [" + signalingState + "]");
26 | }
27 |
28 | @Override
29 | public void onIceConnectionChange(PeerConnection.IceConnectionState iceConnectionState) {
30 | Log.d(logTag, "onIceConnectionChange() called with: iceConnectionState = [" + iceConnectionState + "]");
31 | }
32 |
33 | @Override
34 | public void onIceConnectionReceivingChange(boolean b) {
35 | Log.d(logTag, "onIceConnectionReceivingChange() called with: b = [" + b + "]");
36 | }
37 |
38 | @Override
39 | public void onIceGatheringChange(PeerConnection.IceGatheringState iceGatheringState) {
40 | Log.d(logTag, "onIceGatheringChange() called with: iceGatheringState = [" + iceGatheringState + "]");
41 | }
42 |
43 | @Override
44 | public void onIceCandidate(IceCandidate iceCandidate) {
45 | Log.d(logTag, "onIceCandidate() called with: iceCandidate = [" + iceCandidate + "]");
46 | }
47 |
48 | @Override
49 | public void onIceCandidatesRemoved(IceCandidate[] iceCandidates) {
50 | Log.d(logTag, "onIceCandidatesRemoved() called with: iceCandidates = [" + iceCandidates + "]");
51 | }
52 |
53 | @Override
54 | public void onAddStream(MediaStream mediaStream) {
55 | Log.d(logTag, "onAddStream() called with: mediaStream = [" + mediaStream + "]");
56 | }
57 |
58 | @Override
59 | public void onRemoveStream(MediaStream mediaStream) {
60 | Log.d(logTag, "onRemoveStream() called with: mediaStream = [" + mediaStream + "]");
61 | }
62 |
63 | @Override
64 | public void onDataChannel(DataChannel dataChannel) {
65 | Log.d(logTag, "onDataChannel() called with: dataChannel = [" + dataChannel + "]");
66 | }
67 |
68 | @Override
69 | public void onRenegotiationNeeded() {
70 | Log.d(logTag, "onRenegotiationNeeded() called");
71 | }
72 |
73 | @Override
74 | public void onAddTrack(RtpReceiver rtpReceiver, MediaStream[] mediaStreams) {
75 |
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/app/src/main/java/com/sergiopaniegoblanco/webrtcexampleapp/CustomSdpObserver.java:
--------------------------------------------------------------------------------
1 | package com.sergiopaniegoblanco.webrtcexampleapp;
2 |
3 | import android.util.Log;
4 |
5 | import org.webrtc.SdpObserver;
6 | import org.webrtc.SessionDescription;
7 |
8 | /**
9 | * Created by sergiopaniegoblanco on 30/09/2017.
10 | */
11 |
12 | public class CustomSdpObserver implements SdpObserver {
13 |
14 |
15 | private String tag = this.getClass().getCanonicalName();
16 |
17 | CustomSdpObserver(String logTag) {
18 | this.tag = this.tag + " " + logTag;
19 | }
20 |
21 |
22 | @Override
23 | public void onCreateSuccess(SessionDescription sessionDescription) {
24 | Log.d(tag, "onCreateSuccess() called with: sessionDescription = [" + sessionDescription + "]");
25 | }
26 |
27 | @Override
28 | public void onSetSuccess() {
29 | Log.d(tag, "onSetSuccess() called");
30 | }
31 |
32 | @Override
33 | public void onCreateFailure(String s) {
34 | Log.d(tag, "onCreateFailure() called with: s = [" + s + "]");
35 | }
36 |
37 | @Override
38 | public void onSetFailure(String s) {
39 | Log.d(tag, "onSetFailure() called with: s = [" + s + "]");
40 | }
41 |
42 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sergiopaniegoblanco/webrtcexampleapp/CustomWebSocketListener.java:
--------------------------------------------------------------------------------
1 | package com.sergiopaniegoblanco.webrtcexampleapp;
2 |
3 | import org.json.JSONException;
4 | import org.json.JSONObject;
5 | import org.webrtc.IceCandidate;
6 | import org.webrtc.MediaConstraints;
7 | import org.webrtc.PeerConnection;
8 | import org.webrtc.SessionDescription;
9 |
10 | import okhttp3.Response;
11 | import okhttp3.WebSocket;
12 | import okhttp3.WebSocketListener;
13 | import okio.ByteString;
14 |
15 | /**
16 | * Created by sergiopaniegoblanco on 02/12/2017.
17 | */
18 |
19 | public final class CustomWebSocketListener extends WebSocketListener {
20 |
21 | private MainActivity mainActivity;
22 | private PeerConnection peerConnection;
23 | private WebSocket webSocket;
24 |
25 | public CustomWebSocketListener(MainActivity mainActivity, PeerConnection peerConnection) {
26 | super();
27 | this.mainActivity = mainActivity;
28 | this.peerConnection = peerConnection;
29 | }
30 |
31 | public void setWebSocket(WebSocket webSocket) {
32 | this.webSocket = webSocket;
33 | }
34 |
35 | @Override
36 | public void onOpen(WebSocket webSocket, Response response) {
37 | System.out.println("Open : " + response);
38 | }
39 | @Override
40 | public void onMessage(WebSocket webSocket, String text) {
41 | try {
42 | JSONObject json = new JSONObject(new JSONObject(text).getString("utf8Data"));
43 | if (json.getString("type").equals("candidate")) {
44 | saveIceCandidate(json);
45 | } else {
46 | if (json.getString("type").equals("OFFER")) {
47 | saveOfferAndAnswer(json);
48 | } else if (json.getString("type").equals("ANSWER")) {
49 | saveAnswer(json);
50 | }
51 | }
52 | } catch (JSONException e) {
53 | e.printStackTrace();
54 | }
55 | }
56 | @Override
57 | public void onMessage(WebSocket webSocket, ByteString bytes) {
58 | System.out.println("Receiving bytes : " + bytes.hex());
59 | }
60 | @Override
61 | public void onClosing(WebSocket webSocket, int code, String reason) {
62 | System.out.println("Closing : " + code + " / " + reason);
63 | }
64 | @Override
65 | public void onFailure(WebSocket webSocket, Throwable t, Response response) {
66 | System.out.println("Error : " + t.getMessage());
67 | }
68 |
69 | public void saveIceCandidate(JSONObject json) throws JSONException {
70 | IceCandidate iceCandidate = new IceCandidate(json.getString("id"),Integer.parseInt(json.getString("label")),json.getString("candidate"));
71 | peerConnection.addIceCandidate(iceCandidate);
72 | }
73 |
74 | public void saveAnswer(JSONObject json) throws JSONException {
75 | SessionDescription sessionDescription;
76 | sessionDescription = new SessionDescription(SessionDescription.Type.ANSWER, json.getString("sdp"));
77 | mainActivity.setRemoteDescription(sessionDescription);
78 | mainActivity.setRemoteDescription(sessionDescription);
79 | }
80 |
81 | public void saveOfferAndAnswer(JSONObject json) throws JSONException {
82 | SessionDescription sessionDescription = new SessionDescription(SessionDescription.Type.OFFER, json.getString("sdp"));
83 | peerConnection.setRemoteDescription(new CustomSdpObserver("remoteSetRemoteDesc"), sessionDescription);
84 |
85 | peerConnection.createAnswer(new CustomSdpObserver("remoteCreateOffer") {
86 | @Override
87 | public void onCreateSuccess(SessionDescription sessionDescription) {
88 | super.onCreateSuccess(sessionDescription);
89 | peerConnection.setLocalDescription(new CustomSdpObserver("remoteSetLocalDesc"), sessionDescription);
90 | try {
91 | JSONObject json = new JSONObject();
92 | json.put("type", sessionDescription.type);
93 | json.put("sdp", sessionDescription.description);
94 | webSocket.send(json.toString());
95 | } catch (JSONException e) {
96 | e.printStackTrace();
97 | }
98 | }
99 | }, new MediaConstraints());
100 | }
101 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sergiopaniegoblanco/webrtcexampleapp/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.sergiopaniegoblanco.webrtcexampleapp;
2 |
3 | import android.Manifest;
4 | import android.content.pm.PackageManager;
5 | import android.os.Bundle;
6 | import android.support.v4.app.ActivityCompat;
7 | import android.support.v4.content.ContextCompat;
8 | import android.support.v7.app.AppCompatActivity;
9 | import android.view.View;
10 | import android.widget.Button;
11 |
12 |
13 | import org.json.JSONException;
14 | import org.json.JSONObject;
15 | import org.webrtc.AudioSource;
16 | import org.webrtc.AudioTrack;
17 | import org.webrtc.Camera1Enumerator;
18 | import org.webrtc.CameraEnumerator;
19 | import org.webrtc.EglBase;
20 | import org.webrtc.IceCandidate;
21 | import org.webrtc.MediaConstraints;
22 | import org.webrtc.MediaStream;
23 | import org.webrtc.PeerConnection;
24 | import org.webrtc.PeerConnectionFactory;
25 | import org.webrtc.SessionDescription;
26 | import org.webrtc.SurfaceViewRenderer;
27 | import org.webrtc.VideoCapturer;
28 | import org.webrtc.VideoRenderer;
29 | import org.webrtc.VideoSource;
30 | import org.webrtc.VideoTrack;
31 |
32 |
33 | import java.util.ArrayList;
34 | import java.util.List;
35 |
36 | import butterknife.BindView;
37 | import butterknife.ButterKnife;
38 | import okhttp3.OkHttpClient;
39 | import okhttp3.Request;
40 | import okhttp3.WebSocket;
41 |
42 | public class MainActivity extends AppCompatActivity {
43 |
44 | private final int MY_PERMISSIONS_REQUEST_CAMERA = 100;
45 | private final int MY_PERMISSIONS_REQUEST_RECORD_AUDIO = 101;
46 | private final int MY_PERMISSIONS_REQUEST = 102;
47 |
48 | private PeerConnection localPeer, remotePeer;
49 | private PeerConnectionFactory peerConnectionFactory;
50 | private VideoRenderer remoteRenderer;
51 | private AudioTrack localAudioTrack;
52 | private VideoTrack localVideoTrack;
53 | private String socketAddress = "http://10.0.2.2:1337";
54 | private String session = "/SessionA";
55 | private String secret = "?secret=MY_SECRET";
56 | private OkHttpClient webSocket1;
57 | private WebSocket ws1;
58 | private OkHttpClient webSocket2;
59 | private WebSocket ws2;
60 |
61 | @BindView(R.id.start_call)
62 | Button start_call;
63 | @BindView(R.id.init_call)
64 | Button init_call;
65 | @BindView(R.id.end_call)
66 | Button end_call;
67 | @BindView(R.id.remote_gl_surface_view)
68 | SurfaceViewRenderer remoteVideoView;
69 | @BindView(R.id.local_gl_surface_view)
70 | SurfaceViewRenderer localVideoView;
71 |
72 | @Override
73 | protected void onCreate(Bundle savedInstanceState) {
74 | super.onCreate(savedInstanceState);
75 | setContentView(R.layout.activity_main);
76 | askForPermissions();
77 | ButterKnife.bind(this);
78 | initViews();
79 | }
80 |
81 | public void setRemoteDescription(SessionDescription sessionDescription) {
82 | localPeer.setRemoteDescription(new CustomSdpObserver("localSetRemoteDesc"), sessionDescription);
83 | }
84 |
85 | public void askForPermissions() {
86 | if ((ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
87 | != PackageManager.PERMISSION_GRANTED) &&
88 | (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO)
89 | != PackageManager.PERMISSION_GRANTED)) {
90 | ActivityCompat.requestPermissions(this,
91 | new String[]{Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO},
92 | MY_PERMISSIONS_REQUEST);
93 | } else if (ContextCompat.checkSelfPermission(this,
94 | Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
95 | ActivityCompat.requestPermissions(this,
96 | new String[]{Manifest.permission.RECORD_AUDIO},
97 | MY_PERMISSIONS_REQUEST_RECORD_AUDIO);
98 |
99 | } else if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
100 | != PackageManager.PERMISSION_GRANTED) {
101 | ActivityCompat.requestPermissions(this,
102 | new String[]{Manifest.permission.CAMERA},
103 | MY_PERMISSIONS_REQUEST_CAMERA);
104 | }
105 | }
106 |
107 | public void initViews() {
108 | localVideoView.setMirror(true);
109 | remoteVideoView.setMirror(false);
110 | EglBase rootEglBase = EglBase.create();
111 | localVideoView.init(rootEglBase.getEglBaseContext(), null);
112 | localVideoView.setZOrderMediaOverlay(true);
113 | remoteVideoView.init(rootEglBase.getEglBaseContext(), null);
114 | remoteVideoView.setZOrderMediaOverlay(true);
115 | }
116 |
117 | public void start(View view) {
118 | start_call.setEnabled(false);
119 | init_call.setEnabled(true);
120 | PeerConnectionFactory.initializeAndroidGlobals(this, true);
121 |
122 | PeerConnectionFactory.Options options = new PeerConnectionFactory.Options();
123 | peerConnectionFactory = new PeerConnectionFactory(options);
124 |
125 | VideoCapturer videoGrabberAndroid = createVideoGrabber();
126 | MediaConstraints constraints = new MediaConstraints();
127 |
128 | VideoSource videoSource = peerConnectionFactory.createVideoSource(videoGrabberAndroid);
129 | localVideoTrack = peerConnectionFactory.createVideoTrack("100", videoSource);
130 |
131 | AudioSource audioSource = peerConnectionFactory.createAudioSource(constraints);
132 | localAudioTrack = peerConnectionFactory.createAudioTrack("101", audioSource);
133 |
134 | videoGrabberAndroid.startCapture(1000, 1000, 30);
135 |
136 | final VideoRenderer localRenderer = new VideoRenderer(localVideoView);
137 | localVideoTrack.addRenderer(localRenderer);
138 |
139 | MediaConstraints sdpConstraints = new MediaConstraints();
140 | sdpConstraints.mandatory.add(new MediaConstraints.KeyValuePair("offerToReceiveAudio", "true"));
141 | sdpConstraints.mandatory.add(new MediaConstraints.KeyValuePair("offerToReceiveVideo", "true"));
142 |
143 | createLocalPeerConnection(sdpConstraints);
144 | createLocalSocket();
145 |
146 | MediaStream stream = peerConnectionFactory.createLocalMediaStream("102");
147 | stream.addTrack(localAudioTrack);
148 | stream.addTrack(localVideoTrack);
149 | localPeer.addStream(stream);
150 |
151 | createLocalOffer(sdpConstraints);
152 | }
153 |
154 | public void createLocalPeerConnection(MediaConstraints sdpConstraints) {
155 | final List iceServers = new ArrayList<>();
156 | PeerConnection.IceServer iceServer = new PeerConnection.IceServer("stun:stun.l.google.com:19302");
157 | iceServers.add(iceServer);
158 |
159 | localPeer = peerConnectionFactory.createPeerConnection(iceServers, sdpConstraints, new CustomPeerConnectionObserver("localPeerCreation") {
160 | @Override
161 | public void onIceCandidate(IceCandidate iceCandidate) {
162 | super.onIceCandidate(iceCandidate);
163 | try {
164 | JSONObject json = new JSONObject();
165 | json.put("type", "candidate");
166 | json.put("label", iceCandidate.sdpMLineIndex);
167 | json.put("id", iceCandidate.sdpMid);
168 | json.put("candidate", iceCandidate.sdp);
169 | ws1.send(json.toString());
170 | } catch (JSONException e) {
171 | e.printStackTrace();
172 | }
173 | }
174 | });
175 | }
176 |
177 | public void createLocalSocket() {
178 | Request request = new Request.Builder().url(socketAddress).build();
179 | CustomWebSocketListener listener = new CustomWebSocketListener(this, localPeer);
180 | OkHttpClient.Builder okHttpClientBuilder = new OkHttpClient.Builder();
181 | webSocket1 = okHttpClientBuilder.build();
182 | ws1 = webSocket1.newWebSocket(request, listener);
183 | webSocket1.dispatcher().executorService().shutdown();
184 | }
185 |
186 | public void createLocalOffer(MediaConstraints sdpConstraints) {
187 | localPeer.createOffer(new CustomSdpObserver("localCreateOffer") {
188 | @Override
189 | public void onCreateSuccess(SessionDescription sessionDescription) {
190 | super.onCreateSuccess(sessionDescription);
191 | localPeer.setLocalDescription(new CustomSdpObserver("localSetLocalDesc"), sessionDescription);
192 | try {
193 | JSONObject json = new JSONObject();
194 | json.put("type", sessionDescription.type);
195 | json.put("sdp", sessionDescription.description);
196 | ws1.send(json.toString());
197 | } catch (JSONException e) {
198 | e.printStackTrace();
199 | }
200 | }
201 | }, sdpConstraints);
202 | }
203 |
204 | public void call(View view) {
205 | start_call.setEnabled(false);
206 | init_call.setEnabled(false);
207 | end_call.setEnabled(true);
208 |
209 | createRemotePeerConnection();
210 | createRemoteSocket();
211 | }
212 |
213 | public void createRemotePeerConnection() {
214 | final List iceServers = new ArrayList<>();
215 | PeerConnection.IceServer iceServer = new PeerConnection.IceServer("stun:stun.l.google.com:19302");
216 | iceServers.add(iceServer);
217 |
218 | MediaConstraints sdpConstraints = new MediaConstraints();
219 | sdpConstraints.mandatory.add(new MediaConstraints.KeyValuePair("offerToReceiveAudio", "true"));
220 | sdpConstraints.mandatory.add(new MediaConstraints.KeyValuePair("offerToReceiveVideo", "true"));
221 |
222 | remotePeer = peerConnectionFactory.createPeerConnection(iceServers, sdpConstraints, new CustomPeerConnectionObserver("remotePeerCreation") {
223 |
224 | @Override
225 | public void onIceCandidate(IceCandidate iceCandidate) {
226 | super.onIceCandidate(iceCandidate);
227 | try {
228 | JSONObject json = new JSONObject();
229 | json.put("type", "candidate");
230 | json.put("label", iceCandidate.sdpMLineIndex);
231 | json.put("id", iceCandidate.sdpMid);
232 | json.put("candidate", iceCandidate.sdp);
233 | ws2.send(json.toString());
234 | } catch (JSONException e) {
235 | e.printStackTrace();
236 | }
237 | }
238 |
239 | public void onAddStream(MediaStream mediaStream) {
240 | super.onAddStream(mediaStream);
241 | gotRemoteStream(mediaStream);
242 | }
243 |
244 | @Override
245 | public void onIceGatheringChange(PeerConnection.IceGatheringState iceGatheringState) {
246 | super.onIceGatheringChange(iceGatheringState);
247 |
248 | }
249 | });
250 | }
251 |
252 | public void createRemoteSocket() {
253 | Request request = new Request.Builder().url(socketAddress).build();
254 | CustomWebSocketListener listener = new CustomWebSocketListener(this, remotePeer);
255 | OkHttpClient.Builder okHttpClientBuilder = new OkHttpClient.Builder();
256 | webSocket2 = okHttpClientBuilder.build();
257 | ws2 = webSocket2.newWebSocket(request, listener);
258 | listener.setWebSocket(ws2);
259 | webSocket2.dispatcher().executorService().shutdown();
260 | }
261 |
262 | public void hangup(View view) {
263 | ws1.send("bye");
264 | ws2.send("bye");
265 | localPeer.close();
266 | remotePeer.close();
267 | localPeer = null;
268 | remotePeer = null;
269 | start_call.setEnabled(true);
270 | init_call.setEnabled(false);
271 | end_call.setEnabled(false);
272 | }
273 |
274 | public VideoCapturer createVideoGrabber() {
275 | VideoCapturer videoCapturer;
276 | videoCapturer = createCameraGrabber(new Camera1Enumerator(false));
277 | return videoCapturer;
278 | }
279 |
280 | public VideoCapturer createCameraGrabber(CameraEnumerator enumerator) {
281 | final String[] deviceNames = enumerator.getDeviceNames();
282 |
283 | for (String deviceName : deviceNames) {
284 | if (enumerator.isFrontFacing(deviceName)) {
285 | VideoCapturer videoCapturer = enumerator.createCapturer(deviceName, null);
286 |
287 | if (videoCapturer != null) {
288 | return videoCapturer;
289 | }
290 | }
291 | }
292 |
293 | for (String deviceName : deviceNames) {
294 | if (!enumerator.isFrontFacing(deviceName)) {
295 | VideoCapturer videoCapturer = enumerator.createCapturer(deviceName, null);
296 | if (videoCapturer != null) {
297 | return videoCapturer;
298 | }
299 | }
300 | }
301 |
302 | return null;
303 | }
304 |
305 | private void gotRemoteStream(MediaStream stream) {
306 | final VideoTrack videoTrack = stream.videoTracks.getFirst();
307 | runOnUiThread(new Runnable() {
308 | @Override
309 | public void run() {
310 | try {
311 | remoteRenderer = new VideoRenderer(remoteVideoView);
312 | remoteVideoView.setVisibility(View.VISIBLE);
313 | videoTrack.addRenderer(remoteRenderer);
314 | } catch (Exception e) {
315 | e.printStackTrace();
316 | }
317 | }
318 | });
319 | }
320 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
22 |
23 |
29 |
30 |
36 |
37 |
38 |
39 |
40 |
53 |
54 |
63 |
64 |
74 |
75 |
85 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sergiopaniego/WebRTCAndroidExample/77c017f7d3e8f9b53fe25b6656ab4231aed7ae04/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sergiopaniego/WebRTCAndroidExample/77c017f7d3e8f9b53fe25b6656ab4231aed7ae04/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sergiopaniego/WebRTCAndroidExample/77c017f7d3e8f9b53fe25b6656ab4231aed7ae04/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sergiopaniego/WebRTCAndroidExample/77c017f7d3e8f9b53fe25b6656ab4231aed7ae04/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sergiopaniego/WebRTCAndroidExample/77c017f7d3e8f9b53fe25b6656ab4231aed7ae04/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sergiopaniego/WebRTCAndroidExample/77c017f7d3e8f9b53fe25b6656ab4231aed7ae04/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sergiopaniego/WebRTCAndroidExample/77c017f7d3e8f9b53fe25b6656ab4231aed7ae04/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sergiopaniego/WebRTCAndroidExample/77c017f7d3e8f9b53fe25b6656ab4231aed7ae04/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sergiopaniego/WebRTCAndroidExample/77c017f7d3e8f9b53fe25b6656ab4231aed7ae04/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sergiopaniego/WebRTCAndroidExample/77c017f7d3e8f9b53fe25b6656ab4231aed7ae04/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | WebRTCExampleApp
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/test/java/com/sergiopaniegoblanco/webrtcexampleapp/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.sergiopaniegoblanco.webrtcexampleapp;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:3.0.1'
9 |
10 | // NOTE: Do not place your application dependencies here; they belong
11 | // in the individual module build.gradle files
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | jcenter()
18 | }
19 | }
20 |
21 | task clean(type: Delete) {
22 | delete rootProject.buildDir
23 | }
24 |
--------------------------------------------------------------------------------
/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 | org.gradle.jvmargs=-Xmx1536m
13 |
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. More details, visit
16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
17 | # org.gradle.parallel=true
18 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sergiopaniego/WebRTCAndroidExample/77c017f7d3e8f9b53fe25b6656ab4231aed7ae04/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Nov 06 18:52:03 CET 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | var WebSocketServer = require('websocket').server;
2 | var http = require('http');
3 |
4 | var clients = [ ];
5 | var offer;
6 |
7 | var server = http.createServer(function(request, response) {
8 | // process HTTP request. Since we're writing just WebSockets
9 | // server we don't have to implement anything.
10 | });
11 | server.listen(1337, function() { });
12 |
13 | // create the server
14 | wsServer = new WebSocketServer({
15 | httpServer: server
16 | });
17 |
18 | // WebSocket server
19 | wsServer.on('request', function(request) {
20 | var connection = request.accept(null, request.origin);
21 | var index = clients.push(connection) - 1;
22 | console.log('Index: ' + index)
23 |
24 | // This is the most important callback for us, we'll handle
25 | // all messages from users here.
26 | connection.on('message', function(message) {
27 | var json = JSON.stringify(message, null, 4);
28 | var type = JSON.parse(message['utf8Data'])['type'];
29 | console.log(type);
30 | console.log(clients.length)
31 | if ((type == 'OFFER') && (clients.length == 2)) {
32 | console.log(clients.length)
33 | clients[1].send(JSON.stringify(message))
34 | console.log('OFFER!')
35 | } else if (type == 'ANSWER') {
36 | clients[0].send(JSON.stringify(message))
37 | console.log('ANSWER!')
38 | } else if (type != 'OFFER'){
39 | for (var i=0; i= 2) {
49 | clients[1].send(JSON.stringify(offer))
50 | console.log('OFFER!')
51 | }
52 | });
53 |
54 | connection.on('close', function(connection) {
55 | console.log('Close: ' + connection)
56 | clients = [ ]
57 | });
58 | });
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------