| 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 |
| 11 package org.appspot.apprtc; | 11 package org.appspot.apprtc; |
| 12 | 12 |
| 13 import org.appspot.apprtc.RoomParametersFetcher.RoomParametersFetcherEvents; | 13 import org.appspot.apprtc.RoomParametersFetcher.RoomParametersFetcherEvents; |
| 14 import org.appspot.apprtc.WebSocketChannelClient.WebSocketChannelEvents; | 14 import org.appspot.apprtc.WebSocketChannelClient.WebSocketChannelEvents; |
| 15 import org.appspot.apprtc.WebSocketChannelClient.WebSocketConnectionState; | 15 import org.appspot.apprtc.WebSocketChannelClient.WebSocketConnectionState; |
| 16 import org.appspot.apprtc.util.AsyncHttpURLConnection; | 16 import org.appspot.apprtc.util.AsyncHttpURLConnection; |
| 17 import org.appspot.apprtc.util.AsyncHttpURLConnection.AsyncHttpEvents; | 17 import org.appspot.apprtc.util.AsyncHttpURLConnection.AsyncHttpEvents; |
| 18 import org.appspot.apprtc.util.LooperExecutor; | |
| 19 | 18 |
| 19 import android.os.Handler; |
| 20 import android.os.HandlerThread; |
| 20 import android.util.Log; | 21 import android.util.Log; |
| 21 | 22 |
| 22 import org.json.JSONArray; | 23 import org.json.JSONArray; |
| 23 import org.json.JSONException; | 24 import org.json.JSONException; |
| 24 import org.json.JSONObject; | 25 import org.json.JSONObject; |
| 25 import org.webrtc.IceCandidate; | 26 import org.webrtc.IceCandidate; |
| 26 import org.webrtc.SessionDescription; | 27 import org.webrtc.SessionDescription; |
| 27 | 28 |
| 28 /** | 29 /** |
| 29 * Negotiates signaling for chatting with https://appr.tc "rooms". | 30 * Negotiates signaling for chatting with https://appr.tc "rooms". |
| (...skipping 11 matching lines...) Expand all Loading... |
| 41 private static final String ROOM_JOIN = "join"; | 42 private static final String ROOM_JOIN = "join"; |
| 42 private static final String ROOM_MESSAGE = "message"; | 43 private static final String ROOM_MESSAGE = "message"; |
| 43 private static final String ROOM_LEAVE = "leave"; | 44 private static final String ROOM_LEAVE = "leave"; |
| 44 | 45 |
| 45 private enum ConnectionState { | 46 private enum ConnectionState { |
| 46 NEW, CONNECTED, CLOSED, ERROR | 47 NEW, CONNECTED, CLOSED, ERROR |
| 47 }; | 48 }; |
| 48 private enum MessageType { | 49 private enum MessageType { |
| 49 MESSAGE, LEAVE | 50 MESSAGE, LEAVE |
| 50 }; | 51 }; |
| 51 private final LooperExecutor executor; | 52 private final Handler handler; |
| 52 private boolean initiator; | 53 private boolean initiator; |
| 53 private SignalingEvents events; | 54 private SignalingEvents events; |
| 54 private WebSocketChannelClient wsClient; | 55 private WebSocketChannelClient wsClient; |
| 55 private ConnectionState roomState; | 56 private ConnectionState roomState; |
| 56 private RoomConnectionParameters connectionParameters; | 57 private RoomConnectionParameters connectionParameters; |
| 57 private String messageUrl; | 58 private String messageUrl; |
| 58 private String leaveUrl; | 59 private String leaveUrl; |
| 59 | 60 |
| 60 public WebSocketRTCClient(SignalingEvents events, LooperExecutor executor) { | 61 public WebSocketRTCClient(SignalingEvents events) { |
| 61 this.events = events; | 62 this.events = events; |
| 62 this.executor = executor; | |
| 63 roomState = ConnectionState.NEW; | 63 roomState = ConnectionState.NEW; |
| 64 executor.requestStart(); | 64 final HandlerThread handlerThread = new HandlerThread(TAG); |
| 65 handlerThread.start(); |
| 66 handler = new Handler(handlerThread.getLooper()); |
| 65 } | 67 } |
| 66 | 68 |
| 67 // -------------------------------------------------------------------- | 69 // -------------------------------------------------------------------- |
| 68 // AppRTCClient interface implementation. | 70 // AppRTCClient interface implementation. |
| 69 // Asynchronously connect to an AppRTC room URL using supplied connection | 71 // Asynchronously connect to an AppRTC room URL using supplied connection |
| 70 // parameters, retrieves room parameters and connect to WebSocket server. | 72 // parameters, retrieves room parameters and connect to WebSocket server. |
| 71 @Override | 73 @Override |
| 72 public void connectToRoom(RoomConnectionParameters connectionParameters) { | 74 public void connectToRoom(RoomConnectionParameters connectionParameters) { |
| 73 this.connectionParameters = connectionParameters; | 75 this.connectionParameters = connectionParameters; |
| 74 executor.execute(new Runnable() { | 76 handler.post(new Runnable() { |
| 75 @Override | 77 @Override |
| 76 public void run() { | 78 public void run() { |
| 77 connectToRoomInternal(); | 79 connectToRoomInternal(); |
| 78 } | 80 } |
| 79 }); | 81 }); |
| 80 } | 82 } |
| 81 | 83 |
| 82 @Override | 84 @Override |
| 83 public void disconnectFromRoom() { | 85 public void disconnectFromRoom() { |
| 84 executor.execute(new Runnable() { | 86 handler.post(new Runnable() { |
| 85 @Override | 87 @Override |
| 86 public void run() { | 88 public void run() { |
| 87 disconnectFromRoomInternal(); | 89 disconnectFromRoomInternal(); |
| 90 handler.getLooper().quit(); |
| 88 } | 91 } |
| 89 }); | 92 }); |
| 90 executor.requestStop(); | |
| 91 } | 93 } |
| 92 | 94 |
| 93 // Connects to room - function runs on a local looper thread. | 95 // Connects to room - function runs on a local looper thread. |
| 94 private void connectToRoomInternal() { | 96 private void connectToRoomInternal() { |
| 95 String connectionUrl = getConnectionUrl(connectionParameters); | 97 String connectionUrl = getConnectionUrl(connectionParameters); |
| 96 Log.d(TAG, "Connect to room: " + connectionUrl); | 98 Log.d(TAG, "Connect to room: " + connectionUrl); |
| 97 roomState = ConnectionState.NEW; | 99 roomState = ConnectionState.NEW; |
| 98 wsClient = new WebSocketChannelClient(executor, this); | 100 wsClient = new WebSocketChannelClient(handler, this); |
| 99 | 101 |
| 100 RoomParametersFetcherEvents callbacks = new RoomParametersFetcherEvents() { | 102 RoomParametersFetcherEvents callbacks = new RoomParametersFetcherEvents() { |
| 101 @Override | 103 @Override |
| 102 public void onSignalingParametersReady( | 104 public void onSignalingParametersReady( |
| 103 final SignalingParameters params) { | 105 final SignalingParameters params) { |
| 104 WebSocketRTCClient.this.executor.execute(new Runnable() { | 106 WebSocketRTCClient.this.handler.post(new Runnable() { |
| 105 @Override | 107 @Override |
| 106 public void run() { | 108 public void run() { |
| 107 WebSocketRTCClient.this.signalingParametersReady(params); | 109 WebSocketRTCClient.this.signalingParametersReady(params); |
| 108 } | 110 } |
| 109 }); | 111 }); |
| 110 } | 112 } |
| 111 | 113 |
| 112 @Override | 114 @Override |
| 113 public void onSignalingParametersError(String description) { | 115 public void onSignalingParametersError(String description) { |
| 114 WebSocketRTCClient.this.reportError(description); | 116 WebSocketRTCClient.this.reportError(description); |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 177 events.onConnectedToRoom(signalingParameters); | 179 events.onConnectedToRoom(signalingParameters); |
| 178 | 180 |
| 179 // Connect and register WebSocket client. | 181 // Connect and register WebSocket client. |
| 180 wsClient.connect(signalingParameters.wssUrl, signalingParameters.wssPostUrl)
; | 182 wsClient.connect(signalingParameters.wssUrl, signalingParameters.wssPostUrl)
; |
| 181 wsClient.register(connectionParameters.roomId, signalingParameters.clientId)
; | 183 wsClient.register(connectionParameters.roomId, signalingParameters.clientId)
; |
| 182 } | 184 } |
| 183 | 185 |
| 184 // Send local offer SDP to the other participant. | 186 // Send local offer SDP to the other participant. |
| 185 @Override | 187 @Override |
| 186 public void sendOfferSdp(final SessionDescription sdp) { | 188 public void sendOfferSdp(final SessionDescription sdp) { |
| 187 executor.execute(new Runnable() { | 189 handler.post(new Runnable() { |
| 188 @Override | 190 @Override |
| 189 public void run() { | 191 public void run() { |
| 190 if (roomState != ConnectionState.CONNECTED) { | 192 if (roomState != ConnectionState.CONNECTED) { |
| 191 reportError("Sending offer SDP in non connected state."); | 193 reportError("Sending offer SDP in non connected state."); |
| 192 return; | 194 return; |
| 193 } | 195 } |
| 194 JSONObject json = new JSONObject(); | 196 JSONObject json = new JSONObject(); |
| 195 jsonPut(json, "sdp", sdp.description); | 197 jsonPut(json, "sdp", sdp.description); |
| 196 jsonPut(json, "type", "offer"); | 198 jsonPut(json, "type", "offer"); |
| 197 sendPostMessage(MessageType.MESSAGE, messageUrl, json.toString()); | 199 sendPostMessage(MessageType.MESSAGE, messageUrl, json.toString()); |
| 198 if (connectionParameters.loopback) { | 200 if (connectionParameters.loopback) { |
| 199 // In loopback mode rename this offer to answer and route it back. | 201 // In loopback mode rename this offer to answer and route it back. |
| 200 SessionDescription sdpAnswer = new SessionDescription( | 202 SessionDescription sdpAnswer = new SessionDescription( |
| 201 SessionDescription.Type.fromCanonicalForm("answer"), | 203 SessionDescription.Type.fromCanonicalForm("answer"), |
| 202 sdp.description); | 204 sdp.description); |
| 203 events.onRemoteDescription(sdpAnswer); | 205 events.onRemoteDescription(sdpAnswer); |
| 204 } | 206 } |
| 205 } | 207 } |
| 206 }); | 208 }); |
| 207 } | 209 } |
| 208 | 210 |
| 209 // Send local answer SDP to the other participant. | 211 // Send local answer SDP to the other participant. |
| 210 @Override | 212 @Override |
| 211 public void sendAnswerSdp(final SessionDescription sdp) { | 213 public void sendAnswerSdp(final SessionDescription sdp) { |
| 212 executor.execute(new Runnable() { | 214 handler.post(new Runnable() { |
| 213 @Override | 215 @Override |
| 214 public void run() { | 216 public void run() { |
| 215 if (connectionParameters.loopback) { | 217 if (connectionParameters.loopback) { |
| 216 Log.e(TAG, "Sending answer in loopback mode."); | 218 Log.e(TAG, "Sending answer in loopback mode."); |
| 217 return; | 219 return; |
| 218 } | 220 } |
| 219 JSONObject json = new JSONObject(); | 221 JSONObject json = new JSONObject(); |
| 220 jsonPut(json, "sdp", sdp.description); | 222 jsonPut(json, "sdp", sdp.description); |
| 221 jsonPut(json, "type", "answer"); | 223 jsonPut(json, "type", "answer"); |
| 222 wsClient.send(json.toString()); | 224 wsClient.send(json.toString()); |
| 223 } | 225 } |
| 224 }); | 226 }); |
| 225 } | 227 } |
| 226 | 228 |
| 227 // Send Ice candidate to the other participant. | 229 // Send Ice candidate to the other participant. |
| 228 @Override | 230 @Override |
| 229 public void sendLocalIceCandidate(final IceCandidate candidate) { | 231 public void sendLocalIceCandidate(final IceCandidate candidate) { |
| 230 executor.execute(new Runnable() { | 232 handler.post(new Runnable() { |
| 231 @Override | 233 @Override |
| 232 public void run() { | 234 public void run() { |
| 233 JSONObject json = new JSONObject(); | 235 JSONObject json = new JSONObject(); |
| 234 jsonPut(json, "type", "candidate"); | 236 jsonPut(json, "type", "candidate"); |
| 235 jsonPut(json, "label", candidate.sdpMLineIndex); | 237 jsonPut(json, "label", candidate.sdpMLineIndex); |
| 236 jsonPut(json, "id", candidate.sdpMid); | 238 jsonPut(json, "id", candidate.sdpMid); |
| 237 jsonPut(json, "candidate", candidate.sdp); | 239 jsonPut(json, "candidate", candidate.sdp); |
| 238 if (initiator) { | 240 if (initiator) { |
| 239 // Call initiator sends ice candidates to GAE server. | 241 // Call initiator sends ice candidates to GAE server. |
| 240 if (roomState != ConnectionState.CONNECTED) { | 242 if (roomState != ConnectionState.CONNECTED) { |
| 241 reportError("Sending ICE candidate in non connected state."); | 243 reportError("Sending ICE candidate in non connected state."); |
| 242 return; | 244 return; |
| 243 } | 245 } |
| 244 sendPostMessage(MessageType.MESSAGE, messageUrl, json.toString()); | 246 sendPostMessage(MessageType.MESSAGE, messageUrl, json.toString()); |
| 245 if (connectionParameters.loopback) { | 247 if (connectionParameters.loopback) { |
| 246 events.onRemoteIceCandidate(candidate); | 248 events.onRemoteIceCandidate(candidate); |
| 247 } | 249 } |
| 248 } else { | 250 } else { |
| 249 // Call receiver sends ice candidates to websocket server. | 251 // Call receiver sends ice candidates to websocket server. |
| 250 wsClient.send(json.toString()); | 252 wsClient.send(json.toString()); |
| 251 } | 253 } |
| 252 } | 254 } |
| 253 }); | 255 }); |
| 254 } | 256 } |
| 255 | 257 |
| 256 // Send removed Ice candidates to the other participant. | 258 // Send removed Ice candidates to the other participant. |
| 257 @Override | 259 @Override |
| 258 public void sendLocalIceCandidateRemovals(final IceCandidate[] candidates) { | 260 public void sendLocalIceCandidateRemovals(final IceCandidate[] candidates) { |
| 259 executor.execute(new Runnable() { | 261 handler.post(new Runnable() { |
| 260 @Override | 262 @Override |
| 261 public void run() { | 263 public void run() { |
| 262 JSONObject json = new JSONObject(); | 264 JSONObject json = new JSONObject(); |
| 263 jsonPut(json, "type", "remove-candidates"); | 265 jsonPut(json, "type", "remove-candidates"); |
| 264 JSONArray jsonArray = new JSONArray(); | 266 JSONArray jsonArray = new JSONArray(); |
| 265 for (final IceCandidate candidate : candidates) { | 267 for (final IceCandidate candidate : candidates) { |
| 266 jsonArray.put(toJsonCandidate(candidate)); | 268 jsonArray.put(toJsonCandidate(candidate)); |
| 267 } | 269 } |
| 268 jsonPut(json, "candidates", jsonArray); | 270 jsonPut(json, "candidates", jsonArray); |
| 269 if (initiator) { | 271 if (initiator) { |
| (...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 352 | 354 |
| 353 @Override | 355 @Override |
| 354 public void onWebSocketError(String description) { | 356 public void onWebSocketError(String description) { |
| 355 reportError("WebSocket error: " + description); | 357 reportError("WebSocket error: " + description); |
| 356 } | 358 } |
| 357 | 359 |
| 358 // -------------------------------------------------------------------- | 360 // -------------------------------------------------------------------- |
| 359 // Helper functions. | 361 // Helper functions. |
| 360 private void reportError(final String errorMessage) { | 362 private void reportError(final String errorMessage) { |
| 361 Log.e(TAG, errorMessage); | 363 Log.e(TAG, errorMessage); |
| 362 executor.execute(new Runnable() { | 364 handler.post(new Runnable() { |
| 363 @Override | 365 @Override |
| 364 public void run() { | 366 public void run() { |
| 365 if (roomState != ConnectionState.ERROR) { | 367 if (roomState != ConnectionState.ERROR) { |
| 366 roomState = ConnectionState.ERROR; | 368 roomState = ConnectionState.ERROR; |
| 367 events.onChannelError(errorMessage); | 369 events.onChannelError(errorMessage); |
| 368 } | 370 } |
| 369 } | 371 } |
| 370 }); | 372 }); |
| 371 } | 373 } |
| 372 | 374 |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 421 return json; | 423 return json; |
| 422 } | 424 } |
| 423 | 425 |
| 424 // Converts a JSON candidate to a Java object. | 426 // Converts a JSON candidate to a Java object. |
| 425 IceCandidate toJavaCandidate(JSONObject json) throws JSONException { | 427 IceCandidate toJavaCandidate(JSONObject json) throws JSONException { |
| 426 return new IceCandidate(json.getString("id"), | 428 return new IceCandidate(json.getString("id"), |
| 427 json.getInt("label"), | 429 json.getInt("label"), |
| 428 json.getString("candidate")); | 430 json.getString("candidate")); |
| 429 } | 431 } |
| 430 } | 432 } |
| OLD | NEW |