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 |