| 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 |