diff --git a/talk/examples/android/res/values/strings.xml b/talk/examples/android/res/values/strings.xml
index 08ff54190..c29050521 100644
--- a/talk/examples/android/res/values/strings.xml
+++ b/talk/examples/android/res/values/strings.xml
@@ -57,6 +57,11 @@
Use VP8 VP8 hardware accelerated codec (if available).
true
+ signaling_preference
+ Use WebSocket signaling.
+ Use WebSocket signaling.
+ true
+
Enabled
Disabled
diff --git a/talk/examples/android/res/xml/preferences.xml b/talk/examples/android/res/xml/preferences.xml
index 94894bc22..57c0315d8 100644
--- a/talk/examples/android/res/xml/preferences.xml
+++ b/talk/examples/android/res/xml/preferences.xml
@@ -43,5 +43,10 @@
android:dialogTitle="@string/pref_cpu_usage_detection_dlg"
android:defaultValue="@string/pref_cpu_usage_detection_default" />
+
diff --git a/talk/examples/android/src/org/appspot/apprtc/AppRTCClient.java b/talk/examples/android/src/org/appspot/apprtc/AppRTCClient.java
index 96816a77c..9fbdb3b19 100644
--- a/talk/examples/android/src/org/appspot/apprtc/AppRTCClient.java
+++ b/talk/examples/android/src/org/appspot/apprtc/AppRTCClient.java
@@ -75,14 +75,18 @@ public interface AppRTCClient {
public final String roomId;
public final String clientId;
public final String channelToken;
- public final String offerSdp;
+ public final String wssUrl;
+ public final String wssPostUrl;
+ public final SessionDescription offerSdp;
+ public final List iceCandidates;
public SignalingParameters(
List iceServers,
boolean initiator, MediaConstraints pcConstraints,
MediaConstraints videoConstraints, MediaConstraints audioConstraints,
String roomUrl, String roomId, String clientId,
- String channelToken, String offerSdp ) {
+ String wssUrl, String wssPostUrl, String channelToken,
+ SessionDescription offerSdp, List iceCandidates) {
this.iceServers = iceServers;
this.initiator = initiator;
this.pcConstraints = pcConstraints;
@@ -91,8 +95,11 @@ public interface AppRTCClient {
this.roomUrl = roomUrl;
this.roomId = roomId;
this.clientId = clientId;
+ this.wssUrl = wssUrl;
+ this.wssPostUrl = wssPostUrl;
this.channelToken = channelToken;
this.offerSdp = offerSdp;
+ this.iceCandidates = iceCandidates;
if (channelToken == null || channelToken.length() == 0) {
this.websocketSignaling = true;
} else {
diff --git a/talk/examples/android/src/org/appspot/apprtc/AppRTCDemoActivity.java b/talk/examples/android/src/org/appspot/apprtc/AppRTCDemoActivity.java
index 85e81e7d1..a83e6696e 100644
--- a/talk/examples/android/src/org/appspot/apprtc/AppRTCDemoActivity.java
+++ b/talk/examples/android/src/org/appspot/apprtc/AppRTCDemoActivity.java
@@ -71,7 +71,6 @@ public class AppRTCDemoActivity extends Activity
implements AppRTCClient.SignalingEvents,
PeerConnectionClient.PeerConnectionEvents {
private static final String TAG = "AppRTCClient";
- private final boolean USE_WEBSOCKETS = false;
private PeerConnectionClient pc;
private AppRTCClient appRtcClient;
private SignalingParameters signalingParameters;
@@ -87,8 +86,9 @@ public class AppRTCDemoActivity extends Activity
new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
private TextView hudView;
private TextView encoderStatView;
- private TextView roomName;
+ private TextView roomNameView;
private ImageButton videoScalingButton;
+ private String roomName;
private boolean commandLineRun;
private int runTimeMs;
private int startBitrate;
@@ -119,7 +119,7 @@ public class AppRTCDemoActivity extends Activity
rootView = findViewById(android.R.id.content);
encoderStatView = (TextView)findViewById(R.id.encoder_stat);
menuBar = findViewById(R.id.menubar_fragment);
- roomName = (TextView) findViewById(R.id.room_name);
+ roomNameView = (TextView) findViewById(R.id.room_name);
videoView = (GLSurfaceView) findViewById(R.id.glview);
VideoRendererGui.setView(videoView);
@@ -135,11 +135,11 @@ public class AppRTCDemoActivity extends Activity
? View.INVISIBLE : View.VISIBLE;
encoderStatView.setVisibility(visibility);
menuBar.setVisibility(visibility);
- roomName.setVisibility(visibility);
+ roomNameView.setVisibility(visibility);
if (visibility == View.VISIBLE) {
encoderStatView.bringToFront();
menuBar.bringToFront();
- roomName.bringToFront();
+ roomNameView.bringToFront();
rootView.invalidate();
}
}
@@ -206,6 +206,7 @@ public class AppRTCDemoActivity extends Activity
final Intent intent = getIntent();
Uri url = intent.getData();
+ roomName = intent.getStringExtra(ConnectActivity.EXTRA_ROOMNAME);
boolean loopback = intent.getBooleanExtra(
ConnectActivity.EXTRA_LOOPBACK, false);
commandLineRun = intent.getBooleanExtra(
@@ -213,21 +214,22 @@ public class AppRTCDemoActivity extends Activity
runTimeMs = intent.getIntExtra(ConnectActivity.EXTRA_RUNTIME, 0);
startBitrate = intent.getIntExtra(ConnectActivity.EXTRA_BITRATE, 0);
hwCodec = intent.getBooleanExtra(ConnectActivity.EXTRA_HWCODEC, true);
+ boolean useWebsocket = intent.getBooleanExtra(
+ ConnectActivity.EXTRA_WEBSOCKET, false);
if (url != null) {
- String room = url.getQueryParameter("r");
- if (loopback || (room != null && !room.equals(""))) {
+ if (loopback || (roomName != null && !roomName.equals(""))) {
logAndToast(getString(R.string.connecting_to, url));
- if (USE_WEBSOCKETS) {
+ if (useWebsocket) {
appRtcClient = new WebSocketRTCClient(this);
} else {
appRtcClient = new GAERTCClient(this, this);
}
appRtcClient.connectToRoom(url.toString(), loopback);
if (loopback) {
- roomName.setText("loopback");
+ roomNameView.setText("loopback");
} else {
- roomName.setText(room);
+ roomNameView.setText(roomName);
}
if (commandLineRun && runTimeMs > 0) {
// For command line execution run connection for and exit.
diff --git a/talk/examples/android/src/org/appspot/apprtc/ConnectActivity.java b/talk/examples/android/src/org/appspot/apprtc/ConnectActivity.java
index 9fe6f5191..b8e0e6a73 100644
--- a/talk/examples/android/src/org/appspot/apprtc/ConnectActivity.java
+++ b/talk/examples/android/src/org/appspot/apprtc/ConnectActivity.java
@@ -51,6 +51,7 @@ import android.widget.ListView;
import android.widget.TextView;
import java.util.ArrayList;
+import java.util.Random;
import org.json.JSONArray;
import org.json.JSONException;
@@ -62,16 +63,18 @@ import org.webrtc.MediaCodecVideoEncoder;
*/
public class ConnectActivity extends Activity {
+ public static final String EXTRA_ROOMNAME = "org.appspot.apprtc.ROOMNAME";
public static final String EXTRA_LOOPBACK = "org.appspot.apprtc.LOOPBACK";
public static final String EXTRA_CMDLINE = "org.appspot.apprtc.CMDLINE";
public static final String EXTRA_RUNTIME = "org.appspot.apprtc.RUNTIME";
public static final String EXTRA_BITRATE = "org.appspot.apprtc.BITRATE";
public static final String EXTRA_HWCODEC = "org.appspot.apprtc.HWCODEC";
- private static final String TAG = "ConnectActivity";
- private final boolean USE_WEBSOCKETS = false;
+ public static final String EXTRA_WEBSOCKET = "org.appspot.apprtc.WEBSOCKET";
+ private static final String TAG = "ConnectRTCClient";
private final String APPRTC_SERVER = "https://apprtc.appspot.com";
- private final String APPRTC_WS_SERVER = "https://8-dot-apprtc.appspot.com";
+ private final String APPRTC_WS_SERVER = "https://3-dot-apprtc.appspot.com";
private final int CONNECTION_REQUEST = 1;
+ private static boolean commandLineRun = false;
private ImageButton addRoomButton;
private ImageButton removeRoomButton;
@@ -86,12 +89,11 @@ public class ConnectActivity extends Activity {
private String keyprefBitrateValue;
private String keyprefHwCodec;
private String keyprefCpuUsageDetection;
+ private String keyprefWebsocketSignaling;
private String keyprefRoom;
private String keyprefRoomList;
private ArrayList roomList;
private ArrayAdapter adapter;
- private boolean commandLineRun;
- private int runTimeMs;
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -106,6 +108,7 @@ public class ConnectActivity extends Activity {
keyprefBitrateValue = getString(R.string.pref_startbitratevalue_key);
keyprefHwCodec = getString(R.string.pref_hwcodec_key);
keyprefCpuUsageDetection = getString(R.string.pref_cpu_usage_detection_key);
+ keyprefWebsocketSignaling = getString(R.string.pref_signaling_key);
keyprefRoom = getString(R.string.pref_room_key);
keyprefRoomList = getString(R.string.pref_room_list_key);
@@ -140,17 +143,15 @@ public class ConnectActivity extends Activity {
connectLoopbackButton.setOnClickListener(connectListener);
// If an implicit VIEW intent is launching the app, go directly to that URL.
- commandLineRun = false;
final Intent intent = getIntent();
- if ("android.intent.action.VIEW".equals(intent.getAction())) {
+ if ("android.intent.action.VIEW".equals(intent.getAction()) &&
+ !commandLineRun) {
commandLineRun = true;
boolean loopback = intent.getBooleanExtra(EXTRA_LOOPBACK, false);
- runTimeMs = intent.getIntExtra(EXTRA_RUNTIME, 0);
- String url = intent.getData().toString();
- if (loopback && !url.contains("debug=loopback")) {
- url += "/?debug=loopback";
- }
- connectToRoom(url, loopback, 0, true);
+ int runTimeMs = intent.getIntExtra(EXTRA_RUNTIME, 0);
+ String room = sharedPref.getString(keyprefRoom, "");
+ roomEditText.setText(room);
+ connectToRoom(loopback, runTimeMs);
return;
}
}
@@ -227,108 +228,141 @@ public class ConnectActivity extends Activity {
if (view.getId() == R.id.connect_loopback_button) {
loopback = true;
}
- String url;
- if (USE_WEBSOCKETS) {
- url = APPRTC_WS_SERVER;
- } else {
- url = APPRTC_SERVER;
- }
- if (loopback) {
- url += "/?debug=loopback";
- } else {
- String roomName = getSelectedItem();
- if (roomName == null) {
- roomName = roomEditText.getText().toString();
- }
- url += "/?r=" + roomName;
- }
- // Check HW codec flag.
- boolean hwCodec = sharedPref.getBoolean(keyprefHwCodec,
- Boolean.valueOf(getString(R.string.pref_hwcodec_default)));
- // Add video resolution constraints.
- String parametersResolution = null;
- String parametersFps = null;
- String resolution = sharedPref.getString(keyprefResolution,
- getString(R.string.pref_resolution_default));
- String[] dimensions = resolution.split("[ x]+");
- if (dimensions.length == 2) {
- try {
- int maxWidth = Integer.parseInt(dimensions[0]);
- int maxHeight = Integer.parseInt(dimensions[1]);
- if (maxWidth > 0 && maxHeight > 0) {
- parametersResolution = "minHeight=" + maxHeight + ",maxHeight=" +
- maxHeight + ",minWidth=" + maxWidth + ",maxWidth=" + maxWidth;
- }
- } catch (NumberFormatException e) {
- Log.e(TAG, "Wrong video resolution setting: " + resolution);
- }
- }
- // Add camera fps constraints.
- String fps = sharedPref.getString(keyprefFps,
- getString(R.string.pref_fps_default));
- String[] fpsValues = fps.split("[ x]+");
- if (fpsValues.length == 2) {
- try {
- int cameraFps = Integer.parseInt(fpsValues[0]);
- if (cameraFps > 0) {
- parametersFps = "minFrameRate=" + cameraFps +
- ",maxFrameRate=" + cameraFps;
- }
- } catch (NumberFormatException e) {
- Log.e(TAG, "Wrong camera fps setting: " + fps);
- }
- }
- // Modify connection URL.
- if (parametersResolution != null || parametersFps != null) {
- url += "&video=";
- if (parametersResolution != null) {
- url += parametersResolution;
- if (parametersFps != null) {
- url += ",";
- }
- }
- if (parametersFps != null) {
- url += parametersFps;
- }
- } else {
- if (hwCodec && MediaCodecVideoEncoder.isPlatformSupported()) {
- url += "&hd=true";
- }
- }
- // Get start bitrate.
- int startBitrate = 0;
- String bitrateTypeDefault = getString(R.string.pref_startbitrate_default);
- String bitrateType = sharedPref.getString(
- keyprefBitrateType, bitrateTypeDefault);
- if (!bitrateType.equals(bitrateTypeDefault)) {
- String bitrateValue = sharedPref.getString(keyprefBitrateValue,
- getString(R.string.pref_startbitratevalue_default));
- startBitrate = Integer.parseInt(bitrateValue);
- }
- // Test if CpuOveruseDetection should be disabled. By default is on.
- boolean cpuOveruseDetection = sharedPref.getBoolean(
- keyprefCpuUsageDetection,
- Boolean.valueOf(
- getString(R.string.pref_cpu_usage_detection_default)));
- if (!cpuOveruseDetection) {
- url += "&googCpuOveruseDetection=false";
- }
- // TODO(kjellander): Add support for custom parameters to the URL.
- connectToRoom(url, loopback, startBitrate, hwCodec);
+ commandLineRun = false;
+ connectToRoom(loopback, 0);
}
};
- private void connectToRoom(
- String roomUrl, boolean loopback, int startBitrate, boolean hwCodec) {
- if (validateUrl(roomUrl)) {
- Uri url = Uri.parse(roomUrl);
+ private String appendQueryParameter(String url, String parameter) {
+ String newUrl = url;
+ if (newUrl.contains("?")) {
+ newUrl += "&" + parameter;
+ } else {
+ newUrl += "?" + parameter;
+ }
+ return newUrl;
+ }
+
+ private void connectToRoom(boolean loopback, int runTimeMs) {
+ // Check webSocket signaling flag.
+ boolean useWebsocket = sharedPref.getBoolean(keyprefWebsocketSignaling,
+ Boolean.valueOf(getString(R.string.pref_signaling_default)));
+
+ // Get room name (random for loopback).
+ String roomName;
+ if (loopback) {
+ roomName = Integer.toString((new Random()).nextInt(100000000));
+ } else {
+ roomName = getSelectedItem();
+ if (roomName == null) {
+ roomName = roomEditText.getText().toString();
+ }
+ }
+
+ // Build room URL.
+ String url;
+ if (useWebsocket) {
+ url = APPRTC_WS_SERVER;
+ url += "/register/" + roomName;
+ } else {
+ url = APPRTC_SERVER;
+ url = appendQueryParameter(url, "r=" + roomName);
+ if (loopback) {
+ url = appendQueryParameter(url, "debug=loopback");
+ }
+ }
+
+ // Check HW codec flag.
+ boolean hwCodec = sharedPref.getBoolean(keyprefHwCodec,
+ Boolean.valueOf(getString(R.string.pref_hwcodec_default)));
+
+ // Add video resolution constraints.
+ String parametersResolution = null;
+ String parametersFps = null;
+ String resolution = sharedPref.getString(keyprefResolution,
+ getString(R.string.pref_resolution_default));
+ String[] dimensions = resolution.split("[ x]+");
+ if (dimensions.length == 2) {
+ try {
+ int maxWidth = Integer.parseInt(dimensions[0]);
+ int maxHeight = Integer.parseInt(dimensions[1]);
+ if (maxWidth > 0 && maxHeight > 0) {
+ parametersResolution = "minHeight=" + maxHeight + ",maxHeight=" +
+ maxHeight + ",minWidth=" + maxWidth + ",maxWidth=" + maxWidth;
+ }
+ } catch (NumberFormatException e) {
+ Log.e(TAG, "Wrong video resolution setting: " + resolution);
+ }
+ }
+
+ // Add camera fps constraints.
+ String fps = sharedPref.getString(keyprefFps,
+ getString(R.string.pref_fps_default));
+ String[] fpsValues = fps.split("[ x]+");
+ if (fpsValues.length == 2) {
+ try {
+ int cameraFps = Integer.parseInt(fpsValues[0]);
+ if (cameraFps > 0) {
+ parametersFps = "minFrameRate=" + cameraFps +
+ ",maxFrameRate=" + cameraFps;
+ }
+ } catch (NumberFormatException e) {
+ Log.e(TAG, "Wrong camera fps setting: " + fps);
+ }
+ }
+
+ // Modify connection URL.
+ if (parametersResolution != null || parametersFps != null) {
+ String urlVideoParameters = "video=";
+ if (parametersResolution != null) {
+ urlVideoParameters += parametersResolution;
+ if (parametersFps != null) {
+ urlVideoParameters += ",";
+ }
+ }
+ if (parametersFps != null) {
+ urlVideoParameters += parametersFps;
+ }
+ url = appendQueryParameter(url, urlVideoParameters);
+ } else {
+ if (hwCodec && MediaCodecVideoEncoder.isPlatformSupported()) {
+ url = appendQueryParameter(url, "hd=true");
+ }
+ }
+
+ // Get start bitrate.
+ int startBitrate = 0;
+ String bitrateTypeDefault = getString(R.string.pref_startbitrate_default);
+ String bitrateType = sharedPref.getString(
+ keyprefBitrateType, bitrateTypeDefault);
+ if (!bitrateType.equals(bitrateTypeDefault)) {
+ String bitrateValue = sharedPref.getString(keyprefBitrateValue,
+ getString(R.string.pref_startbitratevalue_default));
+ startBitrate = Integer.parseInt(bitrateValue);
+ }
+
+ // Test if CpuOveruseDetection should be disabled. By default is on.
+ boolean cpuOveruseDetection = sharedPref.getBoolean(
+ keyprefCpuUsageDetection,
+ Boolean.valueOf(
+ getString(R.string.pref_cpu_usage_detection_default)));
+ if (!cpuOveruseDetection) {
+ url = appendQueryParameter(url, "googCpuOveruseDetection=false");
+ }
+
+ // Start AppRTCDemo activity.
+ Log.d(TAG, "Connecting to room " + roomName + " at URL " + url);
+ if (validateUrl(url)) {
+ Uri uri = Uri.parse(url);
Intent intent = new Intent(this, AppRTCDemoActivity.class);
- intent.setData(url);
+ intent.setData(uri);
+ intent.putExtra(EXTRA_ROOMNAME, roomName);
intent.putExtra(EXTRA_LOOPBACK, loopback);
intent.putExtra(EXTRA_CMDLINE, commandLineRun);
intent.putExtra(EXTRA_RUNTIME, runTimeMs);
intent.putExtra(EXTRA_BITRATE, startBitrate);
intent.putExtra(EXTRA_HWCODEC, hwCodec);
+ intent.putExtra(EXTRA_WEBSOCKET, useWebsocket);
startActivityForResult(intent, CONNECTION_REQUEST);
}
}
diff --git a/talk/examples/android/src/org/appspot/apprtc/GAERTCClient.java b/talk/examples/android/src/org/appspot/apprtc/GAERTCClient.java
index fb0f9f07d..c1d7a5184 100644
--- a/talk/examples/android/src/org/appspot/apprtc/GAERTCClient.java
+++ b/talk/examples/android/src/org/appspot/apprtc/GAERTCClient.java
@@ -76,7 +76,7 @@ public class GAERTCClient implements AppRTCClient, RoomParametersFetcherEvents {
*/
@Override
public void connectToRoom(String url, boolean loopback) {
- fetcher = new RoomParametersFetcher(this, loopback);
+ fetcher = new RoomParametersFetcher(this, false, loopback);
fetcher.execute(url);
}
@@ -246,12 +246,12 @@ public class GAERTCClient implements AppRTCClient, RoomParametersFetcherEvents {
}
try {
JSONObject json = new JSONObject(msg);
- String type = (String) json.get("type");
+ String type = json.getString("type");
if (type.equals("candidate")) {
IceCandidate candidate = new IceCandidate(
- (String) json.get("id"),
+ json.getString("id"),
json.getInt("label"),
- (String) json.get("candidate"));
+ json.getString("candidate"));
events.onRemoteIceCandidate(candidate);
} else if (type.equals("answer") || type.equals("offer")) {
SessionDescription sdp = new SessionDescription(
diff --git a/talk/examples/android/src/org/appspot/apprtc/PeerConnectionClient.java b/talk/examples/android/src/org/appspot/apprtc/PeerConnectionClient.java
index 9d53049c0..da94cdbb1 100644
--- a/talk/examples/android/src/org/appspot/apprtc/PeerConnectionClient.java
+++ b/talk/examples/android/src/org/appspot/apprtc/PeerConnectionClient.java
@@ -283,6 +283,7 @@ public class PeerConnectionClient {
}
private void reportError(final String errorMessage) {
+ Log.e(TAG, "Peerconnection error: " + errorMessage);
activity.runOnUiThread(new Runnable() {
public void run() {
events.onPeerConnectionError(errorMessage);
diff --git a/talk/examples/android/src/org/appspot/apprtc/RoomParametersFetcher.java b/talk/examples/android/src/org/appspot/apprtc/RoomParametersFetcher.java
index f49a87386..bdeb9fbcd 100644
--- a/talk/examples/android/src/org/appspot/apprtc/RoomParametersFetcher.java
+++ b/talk/examples/android/src/org/appspot/apprtc/RoomParametersFetcher.java
@@ -33,12 +33,14 @@ import org.appspot.apprtc.AppRTCClient.SignalingParameters;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
+import org.webrtc.IceCandidate;
import org.webrtc.MediaConstraints;
import org.webrtc.PeerConnection;
+import org.webrtc.SessionDescription;
-import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.LinkedList;
@@ -51,6 +53,7 @@ public class RoomParametersFetcher
private static final String TAG = "RoomRTCClient";
private Exception exception = null;
private RoomParametersFetcherEvents events = null;
+ private boolean useNewSignaling;
private boolean loopback;
/**
@@ -69,10 +72,11 @@ public class RoomParametersFetcher
public void onSignalingParametersError(final String description);
}
- public RoomParametersFetcher(
- RoomParametersFetcherEvents events, boolean loopback) {
+ public RoomParametersFetcher(RoomParametersFetcherEvents events,
+ boolean useNewSignaling, boolean loopback) {
super();
this.events = events;
+ this.useNewSignaling = useNewSignaling;
this.loopback = loopback;
}
@@ -115,45 +119,92 @@ public class RoomParametersFetcher
// Fetches |url| and fishes the signaling parameters out of the JSON.
private SignalingParameters getParametersForRoomUrl(String url)
throws IOException, JSONException {
- url = url + "&t=json";
+ if (!useNewSignaling) {
+ if (url.contains("?")) {
+ url += "&t=json";
+ } else {
+ url += "?t=json";
+ }
+ }
Log.d(TAG, "Connecting to room: " + url);
- InputStream responseStream = new BufferedInputStream(
- (new URL(url)).openConnection().getInputStream());
+ HttpURLConnection connection =
+ (HttpURLConnection) new URL(url).openConnection();
+ if (useNewSignaling) {
+ connection.setDoOutput(true);
+ connection.setRequestMethod("POST");
+ } else {
+ connection.setRequestMethod("GET");
+ }
+ connection.setDoInput(true);
+
+ InputStream responseStream = connection.getInputStream();
String response = drainStream(responseStream);
+ responseStream.close();
Log.d(TAG, "Room response: " + response);
JSONObject roomJson = new JSONObject(response);
- if (roomJson.has("error")) {
- JSONArray errors = roomJson.getJSONArray("error_messages");
- throw new IOException(errors.toString());
- }
-
- String roomId = roomJson.getString("room_key");
- String clientId = roomJson.getString("me");
- Log.d(TAG, "RoomId: " + roomId + ". ClientId: " + clientId);
- String channelToken = roomJson.optString("token");
- String offerSdp = roomJson.optString("offer");
- if (offerSdp != null && offerSdp.length() > 0) {
- JSONObject offerJson = new JSONObject(offerSdp);
- offerSdp = offerJson.getString("sdp");
- Log.d(TAG, "SDP type: " + offerJson.getString("type"));
- } else {
- offerSdp = null;
- }
-
- String roomUrl = url.substring(0, url.indexOf('?'));
- Log.d(TAG, "Room url: " + roomUrl);
-
+ String roomId;
+ String clientId;
+ String roomUrl;
+ String channelToken = "";
+ String wssUrl = "";
+ String wssPostUrl = "";
boolean initiator;
- if (loopback) {
- // In loopback mode caller should always be call initiator.
- // TODO(glaznev): remove this once 8-dot-apprtc server will set initiator
- // flag to true for loopback calls.
- initiator = true;
+ LinkedList iceCandidates = null;
+ SessionDescription offerSdp = null;
+
+ if (useNewSignaling) {
+ String result = roomJson.getString("result");
+ if (!result.equals("SUCCESS")) {
+ throw new JSONException(result);
+ }
+ response = roomJson.getString("params");
+ roomJson = new JSONObject(response);
+ roomId = roomJson.getString("room_id");
+ clientId = roomJson.getString("client_id");
+ wssUrl = roomJson.getString("wss_url");
+ wssPostUrl = roomJson.getString("wss_post_url");
+ initiator = (roomJson.getBoolean("is_initiator"));
+ roomUrl = url.substring(0, url.indexOf("/register"));
+ if (!initiator) {
+ iceCandidates = new LinkedList();
+ String messagesString = roomJson.getString("messages");
+ JSONArray messages = new JSONArray(messagesString);
+ for (int i = 0; i < messages.length(); ++i) {
+ String messageString = messages.getString(i);
+ JSONObject message = new JSONObject(messageString);
+ String messageType = message.getString("type");
+ Log.d(TAG, "GAE->C #" + i + " : " + messageString);
+ if (messageType.equals("offer")) {
+ offerSdp = new SessionDescription(
+ SessionDescription.Type.fromCanonicalForm(messageType),
+ message.getString("sdp"));
+ } else if (messageType.equals("candidate")) {
+ IceCandidate candidate = new IceCandidate(
+ message.getString("id"),
+ message.getInt("label"),
+ message.getString("candidate"));
+ iceCandidates.add(candidate);
+ } else {
+ Log.e(TAG, "Unknown message: " + messageString);
+ }
+ }
+ }
} else {
- initiator = roomJson.getInt("initiator") == 1;
+ if (roomJson.has("error")) {
+ JSONArray errors = roomJson.getJSONArray("error_messages");
+ throw new IOException(errors.toString());
+ }
+ roomId = roomJson.getString("room_key");
+ clientId = roomJson.getString("me");
+ channelToken = roomJson.optString("token");
+ initiator = (roomJson.getInt("initiator") == 1);
+ roomUrl = url.substring(0, url.indexOf('?'));
}
+
+ Log.d(TAG, "RoomId: " + roomId + ". ClientId: " + clientId);
Log.d(TAG, "Initiator: " + initiator);
+ Log.d(TAG, "Room url: " + roomUrl);
LinkedList iceServers =
iceServersFromPCConfigJSON(roomJson.getString("pc_config"));
@@ -191,7 +242,8 @@ public class RoomParametersFetcher
iceServers, initiator,
pcConstraints, videoConstraints, audioConstraints,
roomUrl, roomId, clientId,
- channelToken, offerSdp);
+ wssUrl, wssPostUrl, channelToken,
+ offerSdp, iceCandidates);
}
// Mimic Chrome and set DtlsSrtpKeyAgreement to true if not set to false by
diff --git a/talk/examples/android/src/org/appspot/apprtc/SettingsActivity.java b/talk/examples/android/src/org/appspot/apprtc/SettingsActivity.java
index 4ca81ef00..0bed40313 100644
--- a/talk/examples/android/src/org/appspot/apprtc/SettingsActivity.java
+++ b/talk/examples/android/src/org/appspot/apprtc/SettingsActivity.java
@@ -42,6 +42,7 @@ public class SettingsActivity extends Activity
private String keyprefStartBitrateValue;
private String keyprefHwCodec;
private String keyprefCpuUsageDetection;
+ private String keyprefSignaling;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -52,6 +53,7 @@ public class SettingsActivity extends Activity
keyprefStartBitrateValue = getString(R.string.pref_startbitratevalue_key);
keyprefHwCodec = getString(R.string.pref_hwcodec_key);
keyprefCpuUsageDetection = getString(R.string.pref_cpu_usage_detection_key);
+ keyprefSignaling = getString(R.string.pref_signaling_key);
// Display the fragment as the main content.
settingsFragment = new SettingsFragment();
@@ -74,6 +76,7 @@ public class SettingsActivity extends Activity
setBitrateEnable(sharedPreferences);
updateSummaryB(sharedPreferences, keyprefHwCodec);
updateSummaryB(sharedPreferences, keyprefCpuUsageDetection);
+ updateSummaryB(sharedPreferences, keyprefSignaling);
}
@Override
@@ -93,7 +96,7 @@ public class SettingsActivity extends Activity
} else if (key.equals(keyprefStartBitrateValue)) {
updateSummaryBitrate(sharedPreferences, key);
} else if (key.equals(keyprefCpuUsageDetection) ||
- key.equals(keyprefHwCodec)) {
+ key.equals(keyprefHwCodec) || key.equals(keyprefSignaling)) {
updateSummaryB(sharedPreferences, key);
}
if (key.equals(keyprefStartBitrateType)) {
diff --git a/talk/examples/android/src/org/appspot/apprtc/WebSocketChannelClient.java b/talk/examples/android/src/org/appspot/apprtc/WebSocketChannelClient.java
index 170c80700..33aa63b80 100644
--- a/talk/examples/android/src/org/appspot/apprtc/WebSocketChannelClient.java
+++ b/talk/examples/android/src/org/appspot/apprtc/WebSocketChannelClient.java
@@ -140,7 +140,7 @@ public class WebSocketChannelClient {
json.put("cmd", "register");
json.put("roomid", roomID);
json.put("clientid", clientID);
- Log.d(TAG, "WS SEND: " + json.toString());
+ Log.d(TAG, "C->WSS: " + json.toString());
ws.sendTextMessage(json.toString());
state = WebSocketConnectionState.REGISTERED;
// Send any previously accumulated messages.
@@ -176,7 +176,7 @@ public class WebSocketChannelClient {
json.put("cmd", "send");
json.put("msg", message);
message = json.toString();
- Log.d(TAG, "WS SEND: " + message);
+ Log.d(TAG, "C->WSS: " + message);
ws.sendTextMessage(message);
} catch (JSONException e) {
reportError("WebSocket send JSON error: " + e.getMessage());
@@ -279,9 +279,9 @@ public class WebSocketChannelClient {
try {
for (WsHttpMessage wsHttpMessage : wsHttpQueue) {
// Send POST request.
- Log.d(TAG, "WS " + wsHttpMessage.method + " : " +
+ String postUrl = postServerUrl + "/" + roomID + "/" + clientID;
+ Log.d(TAG, "WS " + wsHttpMessage.method + " : " + postUrl + " : " +
wsHttpMessage.message);
- String postUrl = postServerUrl + roomID + "/" + clientID;
HttpURLConnection connection =
(HttpURLConnection) new URL(postUrl).openConnection();
connection.setDoOutput(true);
@@ -333,7 +333,7 @@ public class WebSocketChannelClient {
@Override
public void onTextMessage(String payload) {
- Log.d(TAG, "WS GET: " + payload);
+ Log.d(TAG, "WSS->C: " + payload);
final String message = payload;
uiHandler.post(new Runnable() {
public void run() {
diff --git a/talk/examples/android/src/org/appspot/apprtc/WebSocketRTCClient.java b/talk/examples/android/src/org/appspot/apprtc/WebSocketRTCClient.java
index ef6ef288c..4bfb418d4 100644
--- a/talk/examples/android/src/org/appspot/apprtc/WebSocketRTCClient.java
+++ b/talk/examples/android/src/org/appspot/apprtc/WebSocketRTCClient.java
@@ -32,9 +32,12 @@ import android.os.Looper;
import android.util.Log;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.LinkedList;
+import java.util.Scanner;
import org.appspot.apprtc.RoomParametersFetcher.RoomParametersFetcherEvents;
import org.appspot.apprtc.WebSocketChannelClient.WebSocketChannelEvents;
@@ -57,31 +60,25 @@ import org.webrtc.SessionDescription;
public class WebSocketRTCClient implements AppRTCClient,
RoomParametersFetcherEvents, WebSocketChannelEvents {
private static final String TAG = "WSRTCClient";
- private static final String WSS_SERVER =
- "wss://apprtc-ws.webrtc.org:8089/ws";
- // TODO(glaznev): remove this hard-coded URL and instead get WebSocket http
- // server URL from room response once it will be supported by 8-dot-apprtc.
- private static final String WSS_POST_URL =
- "https://apprtc-ws.webrtc.org:8089/";
private enum ConnectionState {
NEW, CONNECTED, CLOSED, ERROR
};
private final Handler uiHandler;
private boolean loopback;
+ private boolean initiator;
private SignalingEvents events;
- private SignalingParameters signalingParameters;
private WebSocketChannelClient wsClient;
private RoomParametersFetcher fetcher;
private ConnectionState roomState;
- private LinkedList gaePostQueue;
+ private LinkedList postQueue;
private String postMessageUrl;
private String byeMessageUrl;
public WebSocketRTCClient(SignalingEvents events) {
this.events = events;
uiHandler = new Handler(Looper.getMainLooper());
- gaePostQueue = new LinkedList();
+ postQueue = new LinkedList();
}
// --------------------------------------------------------------------
@@ -90,31 +87,38 @@ public class WebSocketRTCClient implements AppRTCClient,
@Override
public void onSignalingParametersReady(final SignalingParameters params) {
Log.d(TAG, "Room connection completed.");
- if (!loopback && !params.initiator && params.offerSdp == null) {
- reportError("Offer SDP is not available.");
- return;
- }
- if (loopback && params.offerSdp != null) {
+ if (loopback && (!params.initiator || params.offerSdp != null)) {
reportError("Loopback room is busy.");
return;
}
- signalingParameters = params;
- postMessageUrl = params.roomUrl + "message?r=" +
- params.roomId + "&u=" + params.clientId;
- byeMessageUrl = params.roomUrl + "bye/" +
+ if (!loopback && !params.initiator && params.offerSdp == null) {
+ Log.w(TAG, "No offer SDP in room response.");
+ }
+ initiator = params.initiator;
+ postMessageUrl = params.roomUrl + "/message/" +
+ params.roomId + "/" + params.clientId;
+ byeMessageUrl = params.roomUrl + "/bye/" +
params.roomId + "/" + params.clientId;
roomState = ConnectionState.CONNECTED;
- wsClient.setClientParameters(
- signalingParameters.roomId, signalingParameters.clientId);
- wsClient.register();
- events.onConnectedToRoom(signalingParameters);
+
+ // Connect to WebSocket server.
+ wsClient.connect(params.wssUrl, params.wssPostUrl);
+ wsClient.setClientParameters(params.roomId, params.clientId);
+
+ // Fire connection and signaling parameters events.
+ events.onConnectedToRoom(params);
events.onChannelOpen();
- if (!signalingParameters.initiator) {
- // For call receiver get sdp offer from room parameters.
- SessionDescription sdp = new SessionDescription(
- SessionDescription.Type.fromCanonicalForm("offer"),
- signalingParameters.offerSdp);
- events.onRemoteDescription(sdp);
+ if (!params.initiator) {
+ // For call receiver get sdp offer and ice candidates
+ // from room parameters.
+ if (params.offerSdp != null) {
+ events.onRemoteDescription(params.offerSdp);
+ }
+ if (params.iceCandidates != null) {
+ for (IceCandidate iceCandidate : params.iceCandidates) {
+ events.onRemoteIceCandidate(iceCandidate);
+ }
+ }
}
}
@@ -128,10 +132,8 @@ public class WebSocketRTCClient implements AppRTCClient,
// All events are called on UI thread.
@Override
public void onWebSocketOpen() {
- Log.d(TAG, "Websocket connection completed.");
- if (roomState == ConnectionState.CONNECTED) {
- wsClient.register();
- }
+ Log.d(TAG, "Websocket connection completed. Registering...");
+ wsClient.register();
}
@Override
@@ -149,15 +151,28 @@ public class WebSocketRTCClient implements AppRTCClient,
String type = json.optString("type");
if (type.equals("candidate")) {
IceCandidate candidate = new IceCandidate(
- (String) json.get("id"),
+ json.getString("id"),
json.getInt("label"),
- (String) json.get("candidate"));
+ json.getString("candidate"));
events.onRemoteIceCandidate(candidate);
} else if (type.equals("answer")) {
- SessionDescription sdp = new SessionDescription(
- SessionDescription.Type.fromCanonicalForm(type),
- (String)json.get("sdp"));
- events.onRemoteDescription(sdp);
+ if (initiator) {
+ SessionDescription sdp = new SessionDescription(
+ SessionDescription.Type.fromCanonicalForm(type),
+ json.getString("sdp"));
+ events.onRemoteDescription(sdp);
+ } else {
+ reportError("Received answer for call initiator: " + msg);
+ }
+ } else if (type.equals("offer")) {
+ if (!initiator) {
+ SessionDescription sdp = new SessionDescription(
+ SessionDescription.Type.fromCanonicalForm(type),
+ json.getString("sdp"));
+ events.onRemoteDescription(sdp);
+ } else {
+ reportError("Received offer for call receiver: " + msg);
+ }
} else if (type.equals("bye")) {
events.onChannelClose();
} else {
@@ -189,32 +204,28 @@ public class WebSocketRTCClient implements AppRTCClient,
// --------------------------------------------------------------------
// AppRTCClient interface implementation.
// Asynchronously connect to an AppRTC room URL, e.g.
- // https://apprtc.appspot.com/?r=NNN, retrieve room parameters
+ // https://apprtc.appspot.com/register/, retrieve room parameters
// and connect to WebSocket server.
@Override
public void connectToRoom(String url, boolean loopback) {
this.loopback = loopback;
+ // Create WebSocket client.
+ wsClient = new WebSocketChannelClient(this);
// Get room parameters.
roomState = ConnectionState.NEW;
- fetcher = new RoomParametersFetcher(this, loopback);
+ fetcher = new RoomParametersFetcher(this, true, loopback);
fetcher.execute(url);
- // Connect to WebSocket server.
- wsClient = new WebSocketChannelClient(this);
- if (!loopback) {
- wsClient.connect(WSS_SERVER, WSS_POST_URL);
- }
}
@Override
public void disconnect() {
Log.d(TAG, "Disconnect. Room state: " + roomState);
- wsClient.disconnect();
if (roomState == ConnectionState.CONNECTED) {
Log.d(TAG, "Closing room.");
- // TODO(glaznev): Remove json bye message sending once new bye will
- // be supported on 8-dot.
- //sendGAEMessage(byeMessageUrl, "");
- sendGAEMessage(postMessageUrl, "{\"type\": \"bye\"}");
+ sendGAEMessage(byeMessageUrl, "");
+ }
+ if (wsClient != null) {
+ wsClient.disconnect();
}
}
@@ -225,17 +236,16 @@ public class WebSocketRTCClient implements AppRTCClient,
// we might want to filter elsewhere.
@Override
public void sendOfferSdp(final SessionDescription sdp) {
+ JSONObject json = new JSONObject();
+ jsonPut(json, "sdp", sdp.description);
+ jsonPut(json, "type", "offer");
+ sendGAEMessage(postMessageUrl, json.toString());
if (loopback) {
- // In loopback mode rename this offer to answer and send it back.
+ // In loopback mode rename this offer to answer and route it back.
SessionDescription sdpAnswer = new SessionDescription(
SessionDescription.Type.fromCanonicalForm("answer"),
sdp.description);
events.onRemoteDescription(sdpAnswer);
- } else {
- JSONObject json = new JSONObject();
- jsonPut(json, "sdp", sdp.description);
- jsonPut(json, "type", "offer");
- sendGAEMessage(postMessageUrl, json.toString());
}
}
@@ -258,18 +268,27 @@ public class WebSocketRTCClient implements AppRTCClient,
// Send Ice candidate to the other participant.
@Override
public void sendLocalIceCandidate(final IceCandidate candidate) {
- if (loopback) {
- events.onRemoteIceCandidate(candidate);
+ JSONObject json = new JSONObject();
+ jsonPut(json, "type", "candidate");
+ jsonPut(json, "label", candidate.sdpMLineIndex);
+ jsonPut(json, "id", candidate.sdpMid);
+ jsonPut(json, "candidate", candidate.sdp);
+ if (initiator) {
+ // Call initiator sends ice candidates to GAE server.
+ if (roomState != ConnectionState.CONNECTED) {
+ reportError("Sending ICE candidate in non connected state.");
+ return;
+ }
+ sendGAEMessage(postMessageUrl, json.toString());
+ if (loopback) {
+ events.onRemoteIceCandidate(candidate);
+ }
} else {
+ // Call receiver sends ice candidates to websocket server.
if (wsClient.getState() != WebSocketConnectionState.REGISTERED) {
reportError("Sending ICE candidate in non registered state.");
return;
}
- JSONObject json = new JSONObject();
- jsonPut(json, "type", "candidate");
- jsonPut(json, "label", candidate.sdpMLineIndex);
- jsonPut(json, "id", candidate.sdpMid);
- jsonPut(json, "candidate", candidate.sdp);
wsClient.send(json.toString());
}
}
@@ -297,8 +316,8 @@ public class WebSocketRTCClient implements AppRTCClient,
}
}
- private class GAEMessage {
- GAEMessage(String postUrl, String message) {
+ private class PostMessage {
+ PostMessage(String postUrl, String message) {
this.postUrl = postUrl;
this.message = message;
}
@@ -308,8 +327,8 @@ public class WebSocketRTCClient implements AppRTCClient,
// Queue a message for sending to the room and send it if already connected.
private synchronized void sendGAEMessage(String url, String message) {
- synchronized (gaePostQueue) {
- gaePostQueue.add(new GAEMessage(url, message));
+ synchronized (postQueue) {
+ postQueue.add(new PostMessage(url, message));
}
(new AsyncTask() {
public Void doInBackground(Void... unused) {
@@ -321,37 +340,58 @@ public class WebSocketRTCClient implements AppRTCClient,
// Send all queued messages if connected to the room.
private void maybeDrainGAEPostQueue() {
- synchronized (gaePostQueue) {
- if (roomState != ConnectionState.CONNECTED) {
- return;
+ if (roomState != ConnectionState.CONNECTED) {
+ return;
+ }
+ PostMessage postMessage = null;
+ while (true) {
+ synchronized (postQueue) {
+ postMessage = postQueue.poll();
+ }
+ if (postMessage == null) {
+ break;
}
try {
- for (GAEMessage gaeMessage : gaePostQueue) {
- Log.d(TAG, "ROOM SEND to " + gaeMessage.postUrl +
- ". Message: " + gaeMessage.message);
- // Check if this is 'bye' message and update room connection state.
- // TODO(glaznev): Uncomment this check and remove check below
- // once new bye message will be supported by 8-dot.
- //if (gaeMessage.postUrl.contains("bye")) {
- // roomState = ConnectionState.CLOSED;
- //}
- JSONObject json = new JSONObject(gaeMessage.message);
- String type = json.optString("type");
- if (type != null && type.equals("bye")) {
- roomState = ConnectionState.CLOSED;
- }
- // Send POST request.
- HttpURLConnection connection =
- (HttpURLConnection) new URL(gaeMessage.postUrl).openConnection();
- connection.setDoOutput(true);
- connection.setRequestProperty(
- "content-type", "text/plain; charset=utf-8");
- connection.getOutputStream().write(
- gaeMessage.message.getBytes("UTF-8"));
- String replyHeader = connection.getHeaderField(null);
- if (!replyHeader.startsWith("HTTP/1.1 200 ")) {
- reportError("Non-200 response to POST: " +
- connection.getHeaderField(null));
+
+ // Check if this is 'bye' message and update room connection state.
+ if (postMessage.postUrl.contains("bye")) {
+ roomState = ConnectionState.CLOSED;
+ Log.d(TAG, "C->GAE: " + postMessage.postUrl);
+ } else {
+ Log.d(TAG, "C->GAE: " + postMessage.message);
+ }
+
+ // Get connection.
+ HttpURLConnection connection =
+ (HttpURLConnection) new URL(postMessage.postUrl).openConnection();
+ byte[] postData = postMessage.message.getBytes("UTF-8");
+ connection.setUseCaches(false);
+ connection.setDoOutput(true);
+ connection.setDoInput(true);
+ connection.setRequestMethod("POST");
+ connection.setFixedLengthStreamingMode(postData.length);
+ connection.setRequestProperty(
+ "content-type", "text/plain; charset=utf-8");
+
+ // Send POST request.
+ OutputStream outStream = connection.getOutputStream();
+ outStream.write(postData);
+ outStream.close();
+
+ // Get response.
+ int responseCode = connection.getResponseCode();
+ if (responseCode != 200) {
+ reportError("Non-200 response to POST: " +
+ connection.getHeaderField(null));
+ }
+ InputStream responseStream = connection.getInputStream();
+ String response = drainStream(responseStream);
+ responseStream.close();
+ if (roomState != ConnectionState.CLOSED) {
+ JSONObject roomJson = new JSONObject(response);
+ String result = roomJson.getString("result");
+ if (!result.equals("SUCCESS")) {
+ reportError("Room POST error: " + result);
}
}
} catch (IOException e) {
@@ -359,9 +399,13 @@ public class WebSocketRTCClient implements AppRTCClient,
} catch (JSONException e) {
reportError("GAE POST JSON error: " + e.getMessage());
}
-
- gaePostQueue.clear();
}
}
+ // Return the contents of an InputStream as a String.
+ private String drainStream(InputStream in) {
+ Scanner s = new Scanner(in).useDelimiter("\\A");
+ return s.hasNext() ? s.next() : "";
+ }
+
}