OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2014 The WebRTC Project Authors. All rights reserved. | 2 * Copyright 2014 The WebRTC Project Authors. All rights reserved. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license | 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 | 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 | 6 * tree. An additional intellectual property rights grant can be found |
7 * in the file PATENTS. All contributing project authors may | 7 * in the file PATENTS. All contributing project authors may |
8 * be found in the AUTHORS file in the root of the source tree. | 8 * be found in the AUTHORS file in the root of the source tree. |
9 */ | 9 */ |
10 | 10 |
(...skipping 18 matching lines...) Expand all Loading... |
29 /** | 29 /** |
30 * Negotiates signaling for chatting with https://appr.tc "rooms". | 30 * Negotiates signaling for chatting with https://appr.tc "rooms". |
31 * Uses the client<->server specifics of the apprtc AppEngine webapp. | 31 * Uses the client<->server specifics of the apprtc AppEngine webapp. |
32 * | 32 * |
33 * <p>To use: create an instance of this object (registering a message handler)
and | 33 * <p>To use: create an instance of this object (registering a message handler)
and |
34 * call connectToRoom(). Once room connection is established | 34 * call connectToRoom(). Once room connection is established |
35 * onConnectedToRoom() callback with room parameters is invoked. | 35 * onConnectedToRoom() callback with room parameters is invoked. |
36 * Messages to other party (with local Ice candidates and answer SDP) can | 36 * Messages to other party (with local Ice candidates and answer SDP) can |
37 * be sent after WebSocket connection is established. | 37 * be sent after WebSocket connection is established. |
38 */ | 38 */ |
39 public class WebSocketRTCClient implements AppRTCClient, | 39 public class WebSocketRTCClient implements AppRTCClient, WebSocketChannelEvents
{ |
40 WebSocketChannelEvents { | |
41 private static final String TAG = "WSRTCClient"; | 40 private static final String TAG = "WSRTCClient"; |
42 private static final String ROOM_JOIN = "join"; | 41 private static final String ROOM_JOIN = "join"; |
43 private static final String ROOM_MESSAGE = "message"; | 42 private static final String ROOM_MESSAGE = "message"; |
44 private static final String ROOM_LEAVE = "leave"; | 43 private static final String ROOM_LEAVE = "leave"; |
45 | 44 |
46 private enum ConnectionState { | 45 private enum ConnectionState { NEW, CONNECTED, CLOSED, ERROR } |
47 NEW, CONNECTED, CLOSED, ERROR | 46 |
48 }; | 47 private enum MessageType { MESSAGE, LEAVE } |
49 private enum MessageType { | 48 |
50 MESSAGE, LEAVE | |
51 }; | |
52 private final Handler handler; | 49 private final Handler handler; |
53 private boolean initiator; | 50 private boolean initiator; |
54 private SignalingEvents events; | 51 private SignalingEvents events; |
55 private WebSocketChannelClient wsClient; | 52 private WebSocketChannelClient wsClient; |
56 private ConnectionState roomState; | 53 private ConnectionState roomState; |
57 private RoomConnectionParameters connectionParameters; | 54 private RoomConnectionParameters connectionParameters; |
58 private String messageUrl; | 55 private String messageUrl; |
59 private String leaveUrl; | 56 private String leaveUrl; |
60 | 57 |
61 public WebSocketRTCClient(SignalingEvents events) { | 58 public WebSocketRTCClient(SignalingEvents events) { |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
94 | 91 |
95 // Connects to room - function runs on a local looper thread. | 92 // Connects to room - function runs on a local looper thread. |
96 private void connectToRoomInternal() { | 93 private void connectToRoomInternal() { |
97 String connectionUrl = getConnectionUrl(connectionParameters); | 94 String connectionUrl = getConnectionUrl(connectionParameters); |
98 Log.d(TAG, "Connect to room: " + connectionUrl); | 95 Log.d(TAG, "Connect to room: " + connectionUrl); |
99 roomState = ConnectionState.NEW; | 96 roomState = ConnectionState.NEW; |
100 wsClient = new WebSocketChannelClient(handler, this); | 97 wsClient = new WebSocketChannelClient(handler, this); |
101 | 98 |
102 RoomParametersFetcherEvents callbacks = new RoomParametersFetcherEvents() { | 99 RoomParametersFetcherEvents callbacks = new RoomParametersFetcherEvents() { |
103 @Override | 100 @Override |
104 public void onSignalingParametersReady( | 101 public void onSignalingParametersReady(final SignalingParameters params) { |
105 final SignalingParameters params) { | |
106 WebSocketRTCClient.this.handler.post(new Runnable() { | 102 WebSocketRTCClient.this.handler.post(new Runnable() { |
107 @Override | 103 @Override |
108 public void run() { | 104 public void run() { |
109 WebSocketRTCClient.this.signalingParametersReady(params); | 105 WebSocketRTCClient.this.signalingParametersReady(params); |
110 } | 106 } |
111 }); | 107 }); |
112 } | 108 } |
113 | 109 |
114 @Override | 110 @Override |
115 public void onSignalingParametersError(String description) { | 111 public void onSignalingParametersError(String description) { |
(...skipping 11 matching lines...) Expand all Loading... |
127 Log.d(TAG, "Closing room."); | 123 Log.d(TAG, "Closing room."); |
128 sendPostMessage(MessageType.LEAVE, leaveUrl, null); | 124 sendPostMessage(MessageType.LEAVE, leaveUrl, null); |
129 } | 125 } |
130 roomState = ConnectionState.CLOSED; | 126 roomState = ConnectionState.CLOSED; |
131 if (wsClient != null) { | 127 if (wsClient != null) { |
132 wsClient.disconnect(true); | 128 wsClient.disconnect(true); |
133 } | 129 } |
134 } | 130 } |
135 | 131 |
136 // Helper functions to get connection, post message and leave message URLs | 132 // Helper functions to get connection, post message and leave message URLs |
137 private String getConnectionUrl( | 133 private String getConnectionUrl(RoomConnectionParameters connectionParameters)
{ |
138 RoomConnectionParameters connectionParameters) { | 134 return connectionParameters.roomUrl + "/" + ROOM_JOIN + "/" + connectionPara
meters.roomId; |
139 return connectionParameters.roomUrl + "/" + ROOM_JOIN + "/" | |
140 + connectionParameters.roomId; | |
141 } | 135 } |
142 | 136 |
143 private String getMessageUrl(RoomConnectionParameters connectionParameters, | 137 private String getMessageUrl( |
144 SignalingParameters signalingParameters) { | 138 RoomConnectionParameters connectionParameters, SignalingParameters signali
ngParameters) { |
145 return connectionParameters.roomUrl + "/" + ROOM_MESSAGE + "/" | 139 return connectionParameters.roomUrl + "/" + ROOM_MESSAGE + "/" + connectionP
arameters.roomId |
146 + connectionParameters.roomId + "/" + signalingParameters.clientId; | 140 + "/" + signalingParameters.clientId; |
147 } | 141 } |
148 | 142 |
149 private String getLeaveUrl(RoomConnectionParameters connectionParameters, | 143 private String getLeaveUrl( |
150 SignalingParameters signalingParameters) { | 144 RoomConnectionParameters connectionParameters, SignalingParameters signali
ngParameters) { |
151 return connectionParameters.roomUrl + "/" + ROOM_LEAVE + "/" | 145 return connectionParameters.roomUrl + "/" + ROOM_LEAVE + "/" + connectionPar
ameters.roomId + "/" |
152 + connectionParameters.roomId + "/" + signalingParameters.clientId; | 146 + signalingParameters.clientId; |
153 } | 147 } |
154 | 148 |
155 // Callback issued when room parameters are extracted. Runs on local | 149 // Callback issued when room parameters are extracted. Runs on local |
156 // looper thread. | 150 // looper thread. |
157 private void signalingParametersReady( | 151 private void signalingParametersReady(final SignalingParameters signalingParam
eters) { |
158 final SignalingParameters signalingParameters) { | |
159 Log.d(TAG, "Room connection completed."); | 152 Log.d(TAG, "Room connection completed."); |
160 if (connectionParameters.loopback | 153 if (connectionParameters.loopback |
161 && (!signalingParameters.initiator | 154 && (!signalingParameters.initiator || signalingParameters.offerSdp != nu
ll)) { |
162 || signalingParameters.offerSdp != null)) { | |
163 reportError("Loopback room is busy."); | 155 reportError("Loopback room is busy."); |
164 return; | 156 return; |
165 } | 157 } |
166 if (!connectionParameters.loopback | 158 if (!connectionParameters.loopback && !signalingParameters.initiator |
167 && !signalingParameters.initiator | |
168 && signalingParameters.offerSdp == null) { | 159 && signalingParameters.offerSdp == null) { |
169 Log.w(TAG, "No offer SDP in room response."); | 160 Log.w(TAG, "No offer SDP in room response."); |
170 } | 161 } |
171 initiator = signalingParameters.initiator; | 162 initiator = signalingParameters.initiator; |
172 messageUrl = getMessageUrl(connectionParameters, signalingParameters); | 163 messageUrl = getMessageUrl(connectionParameters, signalingParameters); |
173 leaveUrl = getLeaveUrl(connectionParameters, signalingParameters); | 164 leaveUrl = getLeaveUrl(connectionParameters, signalingParameters); |
174 Log.d(TAG, "Message URL: " + messageUrl); | 165 Log.d(TAG, "Message URL: " + messageUrl); |
175 Log.d(TAG, "Leave URL: " + leaveUrl); | 166 Log.d(TAG, "Leave URL: " + leaveUrl); |
176 roomState = ConnectionState.CONNECTED; | 167 roomState = ConnectionState.CONNECTED; |
177 | 168 |
(...skipping 15 matching lines...) Expand all Loading... |
193 reportError("Sending offer SDP in non connected state."); | 184 reportError("Sending offer SDP in non connected state."); |
194 return; | 185 return; |
195 } | 186 } |
196 JSONObject json = new JSONObject(); | 187 JSONObject json = new JSONObject(); |
197 jsonPut(json, "sdp", sdp.description); | 188 jsonPut(json, "sdp", sdp.description); |
198 jsonPut(json, "type", "offer"); | 189 jsonPut(json, "type", "offer"); |
199 sendPostMessage(MessageType.MESSAGE, messageUrl, json.toString()); | 190 sendPostMessage(MessageType.MESSAGE, messageUrl, json.toString()); |
200 if (connectionParameters.loopback) { | 191 if (connectionParameters.loopback) { |
201 // In loopback mode rename this offer to answer and route it back. | 192 // In loopback mode rename this offer to answer and route it back. |
202 SessionDescription sdpAnswer = new SessionDescription( | 193 SessionDescription sdpAnswer = new SessionDescription( |
203 SessionDescription.Type.fromCanonicalForm("answer"), | 194 SessionDescription.Type.fromCanonicalForm("answer"), sdp.descripti
on); |
204 sdp.description); | |
205 events.onRemoteDescription(sdpAnswer); | 195 events.onRemoteDescription(sdpAnswer); |
206 } | 196 } |
207 } | 197 } |
208 }); | 198 }); |
209 } | 199 } |
210 | 200 |
211 // Send local answer SDP to the other participant. | 201 // Send local answer SDP to the other participant. |
212 @Override | 202 @Override |
213 public void sendAnswerSdp(final SessionDescription sdp) { | 203 public void sendAnswerSdp(final SessionDescription sdp) { |
214 handler.post(new Runnable() { | 204 handler.post(new Runnable() { |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
256 } | 246 } |
257 | 247 |
258 // Send removed Ice candidates to the other participant. | 248 // Send removed Ice candidates to the other participant. |
259 @Override | 249 @Override |
260 public void sendLocalIceCandidateRemovals(final IceCandidate[] candidates) { | 250 public void sendLocalIceCandidateRemovals(final IceCandidate[] candidates) { |
261 handler.post(new Runnable() { | 251 handler.post(new Runnable() { |
262 @Override | 252 @Override |
263 public void run() { | 253 public void run() { |
264 JSONObject json = new JSONObject(); | 254 JSONObject json = new JSONObject(); |
265 jsonPut(json, "type", "remove-candidates"); | 255 jsonPut(json, "type", "remove-candidates"); |
266 JSONArray jsonArray = new JSONArray(); | 256 JSONArray jsonArray = new JSONArray(); |
267 for (final IceCandidate candidate : candidates) { | 257 for (final IceCandidate candidate : candidates) { |
268 jsonArray.put(toJsonCandidate(candidate)); | 258 jsonArray.put(toJsonCandidate(candidate)); |
269 } | 259 } |
270 jsonPut(json, "candidates", jsonArray); | 260 jsonPut(json, "candidates", jsonArray); |
271 if (initiator) { | 261 if (initiator) { |
272 // Call initiator sends ice candidates to GAE server. | 262 // Call initiator sends ice candidates to GAE server. |
273 if (roomState != ConnectionState.CONNECTED) { | 263 if (roomState != ConnectionState.CONNECTED) { |
274 reportError("Sending ICE candidate removals in non connected state."
); | 264 reportError("Sending ICE candidate removals in non connected state."
); |
275 return; | 265 return; |
276 } | 266 } |
(...skipping 24 matching lines...) Expand all Loading... |
301 String msgText = json.getString("msg"); | 291 String msgText = json.getString("msg"); |
302 String errorText = json.optString("error"); | 292 String errorText = json.optString("error"); |
303 if (msgText.length() > 0) { | 293 if (msgText.length() > 0) { |
304 json = new JSONObject(msgText); | 294 json = new JSONObject(msgText); |
305 String type = json.optString("type"); | 295 String type = json.optString("type"); |
306 if (type.equals("candidate")) { | 296 if (type.equals("candidate")) { |
307 events.onRemoteIceCandidate(toJavaCandidate(json)); | 297 events.onRemoteIceCandidate(toJavaCandidate(json)); |
308 } else if (type.equals("remove-candidates")) { | 298 } else if (type.equals("remove-candidates")) { |
309 JSONArray candidateArray = json.getJSONArray("candidates"); | 299 JSONArray candidateArray = json.getJSONArray("candidates"); |
310 IceCandidate[] candidates = new IceCandidate[candidateArray.length()]; | 300 IceCandidate[] candidates = new IceCandidate[candidateArray.length()]; |
311 for (int i =0; i < candidateArray.length(); ++i) { | 301 for (int i = 0; i < candidateArray.length(); ++i) { |
312 candidates[i] = toJavaCandidate(candidateArray.getJSONObject(i)); | 302 candidates[i] = toJavaCandidate(candidateArray.getJSONObject(i)); |
313 } | 303 } |
314 events.onRemoteIceCandidatesRemoved(candidates); | 304 events.onRemoteIceCandidatesRemoved(candidates); |
315 } else if (type.equals("answer")) { | 305 } else if (type.equals("answer")) { |
316 if (initiator) { | 306 if (initiator) { |
317 SessionDescription sdp = new SessionDescription( | 307 SessionDescription sdp = new SessionDescription( |
318 SessionDescription.Type.fromCanonicalForm(type), | 308 SessionDescription.Type.fromCanonicalForm(type), json.getString(
"sdp")); |
319 json.getString("sdp")); | |
320 events.onRemoteDescription(sdp); | 309 events.onRemoteDescription(sdp); |
321 } else { | 310 } else { |
322 reportError("Received answer for call initiator: " + msg); | 311 reportError("Received answer for call initiator: " + msg); |
323 } | 312 } |
324 } else if (type.equals("offer")) { | 313 } else if (type.equals("offer")) { |
325 if (!initiator) { | 314 if (!initiator) { |
326 SessionDescription sdp = new SessionDescription( | 315 SessionDescription sdp = new SessionDescription( |
327 SessionDescription.Type.fromCanonicalForm(type), | 316 SessionDescription.Type.fromCanonicalForm(type), json.getString(
"sdp")); |
328 json.getString("sdp")); | |
329 events.onRemoteDescription(sdp); | 317 events.onRemoteDescription(sdp); |
330 } else { | 318 } else { |
331 reportError("Received offer for call receiver: " + msg); | 319 reportError("Received offer for call receiver: " + msg); |
332 } | 320 } |
333 } else if (type.equals("bye")) { | 321 } else if (type.equals("bye")) { |
334 events.onChannelClose(); | 322 events.onChannelClose(); |
335 } else { | 323 } else { |
336 reportError("Unexpected WebSocket message: " + msg); | 324 reportError("Unexpected WebSocket message: " + msg); |
337 } | 325 } |
338 } else { | 326 } else { |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
382 } | 370 } |
383 | 371 |
384 // Send SDP or ICE candidate to a room server. | 372 // Send SDP or ICE candidate to a room server. |
385 private void sendPostMessage( | 373 private void sendPostMessage( |
386 final MessageType messageType, final String url, final String message) { | 374 final MessageType messageType, final String url, final String message) { |
387 String logInfo = url; | 375 String logInfo = url; |
388 if (message != null) { | 376 if (message != null) { |
389 logInfo += ". Message: " + message; | 377 logInfo += ". Message: " + message; |
390 } | 378 } |
391 Log.d(TAG, "C->GAE: " + logInfo); | 379 Log.d(TAG, "C->GAE: " + logInfo); |
392 AsyncHttpURLConnection httpConnection = new AsyncHttpURLConnection( | 380 AsyncHttpURLConnection httpConnection = |
393 "POST", url, message, new AsyncHttpEvents() { | 381 new AsyncHttpURLConnection("POST", url, message, new AsyncHttpEvents() { |
394 @Override | 382 @Override |
395 public void onHttpError(String errorMessage) { | 383 public void onHttpError(String errorMessage) { |
396 reportError("GAE POST error: " + errorMessage); | 384 reportError("GAE POST error: " + errorMessage); |
397 } | 385 } |
398 | 386 |
399 @Override | 387 @Override |
400 public void onHttpComplete(String response) { | 388 public void onHttpComplete(String response) { |
401 if (messageType == MessageType.MESSAGE) { | 389 if (messageType == MessageType.MESSAGE) { |
402 try { | 390 try { |
403 JSONObject roomJson = new JSONObject(response); | 391 JSONObject roomJson = new JSONObject(response); |
404 String result = roomJson.getString("result"); | 392 String result = roomJson.getString("result"); |
405 if (!result.equals("SUCCESS")) { | 393 if (!result.equals("SUCCESS")) { |
406 reportError("GAE POST error: " + result); | 394 reportError("GAE POST error: " + result); |
| 395 } |
| 396 } catch (JSONException e) { |
| 397 reportError("GAE POST JSON error: " + e.toString()); |
407 } | 398 } |
408 } catch (JSONException e) { | |
409 reportError("GAE POST JSON error: " + e.toString()); | |
410 } | 399 } |
411 } | 400 } |
412 } | 401 }); |
413 }); | |
414 httpConnection.send(); | 402 httpConnection.send(); |
415 } | 403 } |
416 | 404 |
417 // Converts a Java candidate to a JSONObject. | 405 // Converts a Java candidate to a JSONObject. |
418 private JSONObject toJsonCandidate(final IceCandidate candidate) { | 406 private JSONObject toJsonCandidate(final IceCandidate candidate) { |
419 JSONObject json = new JSONObject(); | 407 JSONObject json = new JSONObject(); |
420 jsonPut(json, "label", candidate.sdpMLineIndex); | 408 jsonPut(json, "label", candidate.sdpMLineIndex); |
421 jsonPut(json, "id", candidate.sdpMid); | 409 jsonPut(json, "id", candidate.sdpMid); |
422 jsonPut(json, "candidate", candidate.sdp); | 410 jsonPut(json, "candidate", candidate.sdp); |
423 return json; | 411 return json; |
424 } | 412 } |
425 | 413 |
426 // Converts a JSON candidate to a Java object. | 414 // Converts a JSON candidate to a Java object. |
427 IceCandidate toJavaCandidate(JSONObject json) throws JSONException { | 415 IceCandidate toJavaCandidate(JSONObject json) throws JSONException { |
428 return new IceCandidate(json.getString("id"), | 416 return new IceCandidate( |
429 json.getInt("label"), | 417 json.getString("id"), json.getInt("label"), json.getString("candidate"))
; |
430 json.getString("candidate")); | |
431 } | 418 } |
432 } | 419 } |
OLD | NEW |