Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(188)

Side by Side Diff: webrtc/examples/androidapp/src/org/appspot/apprtc/DirectRTCClient.java

Issue 1963053002: Direct IP connect functionality for AppRTC Android demo. (Closed) Base URL: https://chromium.googlesource.com/external/webrtc.git@master
Patch Set: Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 /*
2 * Copyright 2015 The WebRTC Project Authors. All rights reserved.
magjed_webrtc 2016/05/10 14:50:02 nit: 2016
sakal 2016/05/11 08:38:50 Done.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11 package org.appspot.apprtc;
12
13 import android.util.Log;
14
15 import org.appspot.apprtc.util.LooperExecutor;
16 import org.json.JSONArray;
17 import org.json.JSONException;
18 import org.json.JSONObject;
19 import org.webrtc.IceCandidate;
20 import org.webrtc.PeerConnection;
21 import org.webrtc.SessionDescription;
22
23 import java.util.LinkedList;
24 import java.util.regex.Matcher;
25 import java.util.regex.Pattern;
26
27 /**
28 * Implementation of AppRTCClient that uses direct TCP connection as the signali ng channel.
29 * This eliminates the need for an external server. This class should not be use d for loopback
30 * connections. They are not supported.
magjed_webrtc 2016/05/10 14:50:02 nit: Merge last two sentences to something like: "
sakal 2016/05/11 08:38:50 Done.
31 */
32 public class DirectRTCClient implements AppRTCClient, TCPChannelClient.TCPChanne lEvents {
33 private static final String TAG = "DirectRTCClient";
34 private static final int DEFAULT_PORT = 8888;
35
36 // Regex pattern used for checking if room id looks like an IP.
37 static final Pattern IP_PATTERN = Pattern.compile(
38 "("
magjed_webrtc 2016/05/10 14:50:02 nit: 4 space indent. Same for other places as well
sakal 2016/05/11 08:38:49 Done.
39 // IPv4
40 + "((\\d+\\.){3}\\d+)|"
41 // IPv6
42 + "\\[((([0-9a-fA-F]{1,4}:)*[0-9a-fA-F]{1,4})?::"
43 + "(([0-9a-fA-F]{1,4}:)*[0-9a-fA-F]{1,4})?)\\]|"
44 + "\\[(([0-9a-fA-F]{1,4}:)*[0-9a-fA-F]{1,4})\\]|"
45 // IPv6 without []
46 + "((([0-9a-fA-F]{1,4}:)*[0-9a-fA-F]{1,4})?::(([0-9a-fA-F]{1,4}:)*[0 -9a-fA-F]{1,4})?)|"
47 + "(([0-9a-fA-F]{1,4}:)*[0-9a-fA-F]{1,4})|"
48 // Literals
49 + "localhost"
50 + ")"
51 // Optional port number
52 + "(:\\d+)?"
53 );
54
55 private final LooperExecutor executor;
56 private final SignalingEvents events;
57 private TCPChannelClient tcpClient;
58 private RoomConnectionParameters connectionParameters;
59
60 private enum ConnectionState {
61 NEW, CONNECTED, CLOSED, ERROR
62 };
63
64 // All alterations of the room state should be done from inside the looper thr ead
65 private ConnectionState roomState;
66
67 public DirectRTCClient(SignalingEvents events) {
68 this.events = events;
69 executor = new LooperExecutor();
70
71 executor.requestStart();
72 roomState = ConnectionState.NEW;
73 }
74
75 /**
76 * Connects to the room, roomId in connectionsParameters is required. roomId m ust be a valid
77 * IP address matching IP_PATTERN.
78 */
79 @Override
80 public void connectToRoom(RoomConnectionParameters connectionParameters) {
81 this.connectionParameters = connectionParameters;
82
83 // Loopback connections should always use the WebSocketRTCClient
magjed_webrtc 2016/05/10 14:50:02 nit: The code is very clear, so I would just remov
sakal 2016/05/11 08:38:50 Done.
84 // They are not supported by this client
85 if (connectionParameters.loopback) {
86 reportError("Loopback connections aren't supported by DirectRTCClient.");
87 }
88
89 executor.execute(new Runnable() {
90 @Override
91 public void run() {
92 connectToRoomInternal();
93 }
94 });
95 }
96
97 @Override
98 public void disconnectFromRoom() {
99 executor.execute(new Runnable() {
100 @Override
101 public void run() {
102 disconnectFromRoomInternal();
103 }
104 });
105 executor.requestStop();
106 }
107
108 /**
109 * Connects to the room.
110 *
111 * Runs on the looper thread.
112 */
113 private void connectToRoomInternal() {
114 this.roomState = ConnectionState.NEW;
115
116 String endpoint = connectionParameters.roomId;
117
118 Matcher matcher = IP_PATTERN.matcher(endpoint);
119 matcher.matches();
magjed_webrtc 2016/05/10 14:50:02 Check if matches() returns false and/or check Ille
sakal 2016/05/11 08:38:50 Done.
120
121 String ip = matcher.group(1);
122 String portStr = endpoint.substring(ip.length());
magjed_webrtc 2016/05/10 14:50:02 nit: I think it's a little bit clearer if you use
sakal 2016/05/11 08:38:49 matcher.group(2) would returns some part inside th
123 int port;
124
125 if (!portStr.isEmpty()) {
126 port = Integer.parseInt(portStr.substring(1));
127 } else {
128 port = DEFAULT_PORT;
129 }
130
131 tcpClient = new TCPChannelClient(executor, this, ip, port);
132 }
133
134 /**
135 * Disconnects from the room.
136 *
137 * Runs on the looper thread.
138 */
139 private void disconnectFromRoomInternal() {
140 roomState = ConnectionState.CLOSED;
141
142 if (tcpClient != null) {
143 tcpClient.disconnect();
144 tcpClient = null;
145 }
146 }
147
148 @Override
149 public void sendOfferSdp(final SessionDescription sdp) {
150 executor.execute(new Runnable() {
151 @Override
152 public void run() {
153 if (roomState != ConnectionState.CONNECTED) {
154 reportError("Sending offer SDP in non connected state.");
155 return;
156 }
157 JSONObject json = new JSONObject();
158 jsonPut(json, "sdp", sdp.description);
159 jsonPut(json, "type", "offer");
160 sendMessage(json.toString());
161 }
162 });
163 }
164
165 @Override
166 public void sendAnswerSdp(final SessionDescription sdp) {
167 executor.execute(new Runnable() {
168 @Override
169 public void run() {
170 JSONObject json = new JSONObject();
171 jsonPut(json, "sdp", sdp.description);
172 jsonPut(json, "type", "answer");
173 sendMessage(json.toString());
174 }
175 });
176 }
177
178 @Override
179 public void sendLocalIceCandidate(final IceCandidate candidate) {
180 executor.execute(new Runnable() {
181 @Override
182 public void run() {
183 JSONObject json = new JSONObject();
184 jsonPut(json, "type", "candidate");
185 jsonPut(json, "label", candidate.sdpMLineIndex);
186 jsonPut(json, "id", candidate.sdpMid);
187 jsonPut(json, "candidate", candidate.sdp);
188
189 if (roomState != ConnectionState.CONNECTED) {
190 reportError("Sending ICE candidate in non connected state.");
191 return;
192 }
193 sendMessage(json.toString());
194 }
195 });
196 }
197
198 /** Send removed Ice candidates to the other participant. */
199 @Override
200 public void sendLocalIceCandidateRemovals(final IceCandidate[] candidates) {
201 executor.execute(new Runnable() {
202 @Override
203 public void run() {
204 JSONObject json = new JSONObject();
205 jsonPut(json, "type", "remove-candidates");
206 JSONArray jsonArray = new JSONArray();
207 for (final IceCandidate candidate : candidates) {
208 jsonArray.put(toJsonCandidate(candidate));
209 }
210 jsonPut(json, "candidates", jsonArray);
211
212 if (roomState != ConnectionState.CONNECTED) {
213 reportError("Sending ICE candidate removals in non connected state.");
214 return;
215 }
216 sendMessage(json.toString());
217 }
218 });
219 }
220
221 // -------------------------------------------------------------------
222 // TCPChannelClient event handlers
223
224 /**
225 * If the client is the server side, this will trigger onConnectedToRoom.
226 *
227 * @param isServer
228 */
229 @Override
230 public void onTCPConnected(boolean isServer) {
231 if (isServer) {
232 roomState = ConnectionState.CONNECTED;
233
234 SignalingParameters parameters = new SignalingParameters(
235 new LinkedList<PeerConnection.IceServer>(), isServer, null, null, null, null, null);
magjed_webrtc 2016/05/10 14:50:02 nit: Add comments about the null parameters, e.g.
sakal 2016/05/11 08:38:50 Done.
236 events.onConnectedToRoom(parameters);
237 }
238 }
239
240 @Override
241 public void onTCPMessage(String msg) {
242 try {
243 JSONObject json = new JSONObject(msg);
244 String type = json.optString("type");
245 if (type.equals("candidate")) {
246 events.onRemoteIceCandidate(toJavaCandidate(json));
247 } else if (type.equals("remove-candidates")) {
248 JSONArray candidateArray = json.getJSONArray("candidates");
249 IceCandidate[] candidates = new IceCandidate[candidateArray.length()];
250 for (int i = 0; i < candidateArray.length(); ++i) {
251 candidates[i] = toJavaCandidate(candidateArray.getJSONObject(i));
252 }
253 events.onRemoteIceCandidatesRemoved(candidates);
254 } else if (type.equals("answer")) {
255 SessionDescription sdp = new SessionDescription(
256 SessionDescription.Type.fromCanonicalForm(type),
257 json.getString("sdp"));
258 events.onRemoteDescription(sdp);
259 } else if (type.equals("offer")) {
260 SessionDescription sdp = new SessionDescription(
261 SessionDescription.Type.fromCanonicalForm(type),
262 json.getString("sdp"));
263
264 SignalingParameters parameters = new SignalingParameters(
265 new LinkedList<PeerConnection.IceServer>(), false, null, null, n ull, sdp, null);
magjed_webrtc 2016/05/10 14:50:02 ditto: Add comments about the null parameters, e.g
sakal 2016/05/11 08:38:49 Done.
266 roomState = ConnectionState.CONNECTED;
267 events.onConnectedToRoom(parameters);
268 } else if (type.equals("bye")) {
269 events.onChannelClose();
270 } else {
271 reportError("Unexpected TCP message: " + msg);
272 }
273 } catch (JSONException e) {
274 reportError("TCP message JSON parsing error: " + e.toString());
275 }
276 }
277
278 @Override
279 public void onTCPError(String description) {
280 reportError("TCP connection error: " + description);
281 }
282
283 @Override
284 public void onTCPClose() {
285 events.onChannelClose();
286 }
287
288 // --------------------------------------------------------------------
289 // Helper functions.
290 private void reportError(final String errorMessage) {
291 Log.e(TAG, errorMessage);
292 executor.execute(new Runnable() {
293 @Override
294 public void run() {
295 if (roomState != ConnectionState.ERROR) {
296 roomState = ConnectionState.ERROR;
297 events.onChannelError(errorMessage);
298 }
299 }
300 });
301 }
302
303 private void sendMessage(final String message) {
304 executor.execute(new Runnable() {
305 @Override
306 public void run() {
307 tcpClient.send(message);
308 }
309 });
310 }
311
312 // Put a |key|->|value| mapping in |json|.
313 private static void jsonPut(JSONObject json, String key, Object value) {
314 try {
315 json.put(key, value);
316 } catch (JSONException e) {
317 throw new RuntimeException(e);
318 }
319 }
320
321 // Converts a Java candidate to a JSONObject.
322 private static JSONObject toJsonCandidate(final IceCandidate candidate) {
323 JSONObject json = new JSONObject();
324 jsonPut(json, "label", candidate.sdpMLineIndex);
325 jsonPut(json, "id", candidate.sdpMid);
326 jsonPut(json, "candidate", candidate.sdp);
327 return json;
328 }
329
330 // Converts a JSON candidate to a Java object.
331 private static IceCandidate toJavaCandidate(JSONObject json) throws JSONExcept ion {
332 return new IceCandidate(json.getString("id"),
333 json.getInt("label"),
334 json.getString("candidate"));
335 }
336 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698