| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * libjingle | |
| 3 * Copyright 2014 Google Inc. | |
| 4 * | |
| 5 * Redistribution and use in source and binary forms, with or without | |
| 6 * modification, are permitted provided that the following conditions are met: | |
| 7 * | |
| 8 * 1. Redistributions of source code must retain the above copyright notice, | |
| 9 * this list of conditions and the following disclaimer. | |
| 10 * 2. Redistributions in binary form must reproduce the above copyright notice, | |
| 11 * this list of conditions and the following disclaimer in the documentation | |
| 12 * and/or other materials provided with the distribution. | |
| 13 * 3. The name of the author may not be used to endorse or promote products | |
| 14 * derived from this software without specific prior written permission. | |
| 15 * | |
| 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED | |
| 17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
| 18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | |
| 19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
| 20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
| 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | |
| 22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | |
| 23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | |
| 24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF | |
| 25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| 26 */ | |
| 27 | |
| 28 package org.appspot.apprtc; | |
| 29 | |
| 30 import org.appspot.apprtc.AppRTCClient.SignalingParameters; | |
| 31 import org.appspot.apprtc.util.AsyncHttpURLConnection; | |
| 32 import org.appspot.apprtc.util.AsyncHttpURLConnection.AsyncHttpEvents; | |
| 33 | |
| 34 import android.util.Log; | |
| 35 | |
| 36 import org.json.JSONArray; | |
| 37 import org.json.JSONException; | |
| 38 import org.json.JSONObject; | |
| 39 import org.webrtc.IceCandidate; | |
| 40 import org.webrtc.PeerConnection; | |
| 41 import org.webrtc.SessionDescription; | |
| 42 | |
| 43 import java.io.IOException; | |
| 44 import java.io.InputStream; | |
| 45 import java.net.HttpURLConnection; | |
| 46 import java.net.URL; | |
| 47 import java.util.LinkedList; | |
| 48 import java.util.Scanner; | |
| 49 | |
| 50 /** | |
| 51 * AsyncTask that converts an AppRTC room URL into the set of signaling | |
| 52 * parameters to use with that room. | |
| 53 */ | |
| 54 public class RoomParametersFetcher { | |
| 55 private static final String TAG = "RoomRTCClient"; | |
| 56 private static final int TURN_HTTP_TIMEOUT_MS = 5000; | |
| 57 private final RoomParametersFetcherEvents events; | |
| 58 private final String roomUrl; | |
| 59 private final String roomMessage; | |
| 60 private AsyncHttpURLConnection httpConnection; | |
| 61 | |
| 62 /** | |
| 63 * Room parameters fetcher callbacks. | |
| 64 */ | |
| 65 public static interface RoomParametersFetcherEvents { | |
| 66 /** | |
| 67 * Callback fired once the room's signaling parameters | |
| 68 * SignalingParameters are extracted. | |
| 69 */ | |
| 70 public void onSignalingParametersReady(final SignalingParameters params); | |
| 71 | |
| 72 /** | |
| 73 * Callback for room parameters extraction error. | |
| 74 */ | |
| 75 public void onSignalingParametersError(final String description); | |
| 76 } | |
| 77 | |
| 78 public RoomParametersFetcher(String roomUrl, String roomMessage, | |
| 79 final RoomParametersFetcherEvents events) { | |
| 80 this.roomUrl = roomUrl; | |
| 81 this.roomMessage = roomMessage; | |
| 82 this.events = events; | |
| 83 } | |
| 84 | |
| 85 public void makeRequest() { | |
| 86 Log.d(TAG, "Connecting to room: " + roomUrl); | |
| 87 httpConnection = new AsyncHttpURLConnection( | |
| 88 "POST", roomUrl, roomMessage, | |
| 89 new AsyncHttpEvents() { | |
| 90 @Override | |
| 91 public void onHttpError(String errorMessage) { | |
| 92 Log.e(TAG, "Room connection error: " + errorMessage); | |
| 93 events.onSignalingParametersError(errorMessage); | |
| 94 } | |
| 95 | |
| 96 @Override | |
| 97 public void onHttpComplete(String response) { | |
| 98 roomHttpResponseParse(response); | |
| 99 } | |
| 100 }); | |
| 101 httpConnection.send(); | |
| 102 } | |
| 103 | |
| 104 private void roomHttpResponseParse(String response) { | |
| 105 Log.d(TAG, "Room response: " + response); | |
| 106 try { | |
| 107 LinkedList<IceCandidate> iceCandidates = null; | |
| 108 SessionDescription offerSdp = null; | |
| 109 JSONObject roomJson = new JSONObject(response); | |
| 110 | |
| 111 String result = roomJson.getString("result"); | |
| 112 if (!result.equals("SUCCESS")) { | |
| 113 events.onSignalingParametersError("Room response error: " + result); | |
| 114 return; | |
| 115 } | |
| 116 response = roomJson.getString("params"); | |
| 117 roomJson = new JSONObject(response); | |
| 118 String roomId = roomJson.getString("room_id"); | |
| 119 String clientId = roomJson.getString("client_id"); | |
| 120 String wssUrl = roomJson.getString("wss_url"); | |
| 121 String wssPostUrl = roomJson.getString("wss_post_url"); | |
| 122 boolean initiator = (roomJson.getBoolean("is_initiator")); | |
| 123 if (!initiator) { | |
| 124 iceCandidates = new LinkedList<IceCandidate>(); | |
| 125 String messagesString = roomJson.getString("messages"); | |
| 126 JSONArray messages = new JSONArray(messagesString); | |
| 127 for (int i = 0; i < messages.length(); ++i) { | |
| 128 String messageString = messages.getString(i); | |
| 129 JSONObject message = new JSONObject(messageString); | |
| 130 String messageType = message.getString("type"); | |
| 131 Log.d(TAG, "GAE->C #" + i + " : " + messageString); | |
| 132 if (messageType.equals("offer")) { | |
| 133 offerSdp = new SessionDescription( | |
| 134 SessionDescription.Type.fromCanonicalForm(messageType), | |
| 135 message.getString("sdp")); | |
| 136 } else if (messageType.equals("candidate")) { | |
| 137 IceCandidate candidate = new IceCandidate( | |
| 138 message.getString("id"), | |
| 139 message.getInt("label"), | |
| 140 message.getString("candidate")); | |
| 141 iceCandidates.add(candidate); | |
| 142 } else { | |
| 143 Log.e(TAG, "Unknown message: " + messageString); | |
| 144 } | |
| 145 } | |
| 146 } | |
| 147 Log.d(TAG, "RoomId: " + roomId + ". ClientId: " + clientId); | |
| 148 Log.d(TAG, "Initiator: " + initiator); | |
| 149 Log.d(TAG, "WSS url: " + wssUrl); | |
| 150 Log.d(TAG, "WSS POST url: " + wssPostUrl); | |
| 151 | |
| 152 LinkedList<PeerConnection.IceServer> iceServers = | |
| 153 iceServersFromPCConfigJSON(roomJson.getString("pc_config")); | |
| 154 boolean isTurnPresent = false; | |
| 155 for (PeerConnection.IceServer server : iceServers) { | |
| 156 Log.d(TAG, "IceServer: " + server); | |
| 157 if (server.uri.startsWith("turn:")) { | |
| 158 isTurnPresent = true; | |
| 159 break; | |
| 160 } | |
| 161 } | |
| 162 // Request TURN servers. | |
| 163 if (!isTurnPresent) { | |
| 164 LinkedList<PeerConnection.IceServer> turnServers = | |
| 165 requestTurnServers(roomJson.getString("turn_url")); | |
| 166 for (PeerConnection.IceServer turnServer : turnServers) { | |
| 167 Log.d(TAG, "TurnServer: " + turnServer); | |
| 168 iceServers.add(turnServer); | |
| 169 } | |
| 170 } | |
| 171 | |
| 172 SignalingParameters params = new SignalingParameters( | |
| 173 iceServers, initiator, | |
| 174 clientId, wssUrl, wssPostUrl, | |
| 175 offerSdp, iceCandidates); | |
| 176 events.onSignalingParametersReady(params); | |
| 177 } catch (JSONException e) { | |
| 178 events.onSignalingParametersError( | |
| 179 "Room JSON parsing error: " + e.toString()); | |
| 180 } catch (IOException e) { | |
| 181 events.onSignalingParametersError("Room IO error: " + e.toString()); | |
| 182 } | |
| 183 } | |
| 184 | |
| 185 // Requests & returns a TURN ICE Server based on a request URL. Must be run | |
| 186 // off the main thread! | |
| 187 private LinkedList<PeerConnection.IceServer> requestTurnServers(String url) | |
| 188 throws IOException, JSONException { | |
| 189 LinkedList<PeerConnection.IceServer> turnServers = | |
| 190 new LinkedList<PeerConnection.IceServer>(); | |
| 191 Log.d(TAG, "Request TURN from: " + url); | |
| 192 HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnecti
on(); | |
| 193 connection.setConnectTimeout(TURN_HTTP_TIMEOUT_MS); | |
| 194 connection.setReadTimeout(TURN_HTTP_TIMEOUT_MS); | |
| 195 int responseCode = connection.getResponseCode(); | |
| 196 if (responseCode != 200) { | |
| 197 throw new IOException("Non-200 response when requesting TURN server from " | |
| 198 + url + " : " + connection.getHeaderField(null)); | |
| 199 } | |
| 200 InputStream responseStream = connection.getInputStream(); | |
| 201 String response = drainStream(responseStream); | |
| 202 connection.disconnect(); | |
| 203 Log.d(TAG, "TURN response: " + response); | |
| 204 JSONObject responseJSON = new JSONObject(response); | |
| 205 String username = responseJSON.getString("username"); | |
| 206 String password = responseJSON.getString("password"); | |
| 207 JSONArray turnUris = responseJSON.getJSONArray("uris"); | |
| 208 for (int i = 0; i < turnUris.length(); i++) { | |
| 209 String uri = turnUris.getString(i); | |
| 210 turnServers.add(new PeerConnection.IceServer(uri, username, password)); | |
| 211 } | |
| 212 return turnServers; | |
| 213 } | |
| 214 | |
| 215 // Return the list of ICE servers described by a WebRTCPeerConnection | |
| 216 // configuration string. | |
| 217 private LinkedList<PeerConnection.IceServer> iceServersFromPCConfigJSON( | |
| 218 String pcConfig) throws JSONException { | |
| 219 JSONObject json = new JSONObject(pcConfig); | |
| 220 JSONArray servers = json.getJSONArray("iceServers"); | |
| 221 LinkedList<PeerConnection.IceServer> ret = | |
| 222 new LinkedList<PeerConnection.IceServer>(); | |
| 223 for (int i = 0; i < servers.length(); ++i) { | |
| 224 JSONObject server = servers.getJSONObject(i); | |
| 225 String url = server.getString("urls"); | |
| 226 String credential = | |
| 227 server.has("credential") ? server.getString("credential") : ""; | |
| 228 ret.add(new PeerConnection.IceServer(url, "", credential)); | |
| 229 } | |
| 230 return ret; | |
| 231 } | |
| 232 | |
| 233 // Return the contents of an InputStream as a String. | |
| 234 private static String drainStream(InputStream in) { | |
| 235 Scanner s = new Scanner(in).useDelimiter("\\A"); | |
| 236 return s.hasNext() ? s.next() : ""; | |
| 237 } | |
| 238 | |
| 239 } | |
| OLD | NEW |