Support new WebSocket signaling format.
- Support new GAE message format and new signaling sequence, which allows connection to 3-dot-apprtc server. - Add UI setting to switch between GAE / WebSockets signaling. - Some clean ups to better support command line application execution. BUG=3937,3995,4041 R=jiayl@webrtc.org, tkchin@webrtc.org Review URL: https://webrtc-codereview.appspot.com/27319004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@7813 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
parent
0b38478885
commit
369746bcb8
@ -57,6 +57,11 @@
|
||||
<string name="pref_hwcodec_dlg">Use VP8 VP8 hardware accelerated codec (if available).</string>
|
||||
<string name="pref_hwcodec_default">true</string>
|
||||
|
||||
<string name="pref_signaling_key">signaling_preference</string>
|
||||
<string name="pref_signaling_title">Use WebSocket signaling.</string>
|
||||
<string name="pref_signaling_dlg">Use WebSocket signaling.</string>
|
||||
<string name="pref_signaling_default">true</string>
|
||||
|
||||
<string name="pref_value_enabled">Enabled</string>
|
||||
<string name="pref_value_disabled">Disabled</string>
|
||||
|
||||
|
@ -43,5 +43,10 @@
|
||||
android:dialogTitle="@string/pref_cpu_usage_detection_dlg"
|
||||
android:defaultValue="@string/pref_cpu_usage_detection_default" />
|
||||
|
||||
<CheckBoxPreference
|
||||
android:key="@string/pref_signaling_key"
|
||||
android:title="@string/pref_signaling_title"
|
||||
android:dialogTitle="@string/pref_signaling_dlg"
|
||||
android:defaultValue="@string/pref_signaling_default" />
|
||||
|
||||
</PreferenceScreen>
|
||||
|
@ -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<IceCandidate> iceCandidates;
|
||||
|
||||
public SignalingParameters(
|
||||
List<PeerConnection.IceServer> 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<IceCandidate> 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 {
|
||||
|
@ -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 <runTimeMs> and exit.
|
||||
|
@ -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<String> roomList;
|
||||
private ArrayAdapter<String> 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,24 +228,54 @@ 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;
|
||||
commandLineRun = false;
|
||||
connectToRoom(loopback, 0);
|
||||
}
|
||||
if (loopback) {
|
||||
url += "/?debug=loopback";
|
||||
};
|
||||
|
||||
private String appendQueryParameter(String url, String parameter) {
|
||||
String newUrl = url;
|
||||
if (newUrl.contains("?")) {
|
||||
newUrl += "&" + parameter;
|
||||
} else {
|
||||
String roomName = getSelectedItem();
|
||||
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();
|
||||
}
|
||||
url += "/?r=" + roomName;
|
||||
}
|
||||
|
||||
// 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;
|
||||
@ -263,6 +294,7 @@ public class ConnectActivity extends Activity {
|
||||
Log.e(TAG, "Wrong video resolution setting: " + resolution);
|
||||
}
|
||||
}
|
||||
|
||||
// Add camera fps constraints.
|
||||
String fps = sharedPref.getString(keyprefFps,
|
||||
getString(R.string.pref_fps_default));
|
||||
@ -278,23 +310,26 @@ public class ConnectActivity extends Activity {
|
||||
Log.e(TAG, "Wrong camera fps setting: " + fps);
|
||||
}
|
||||
}
|
||||
|
||||
// Modify connection URL.
|
||||
if (parametersResolution != null || parametersFps != null) {
|
||||
url += "&video=";
|
||||
String urlVideoParameters = "video=";
|
||||
if (parametersResolution != null) {
|
||||
url += parametersResolution;
|
||||
urlVideoParameters += parametersResolution;
|
||||
if (parametersFps != null) {
|
||||
url += ",";
|
||||
urlVideoParameters += ",";
|
||||
}
|
||||
}
|
||||
if (parametersFps != null) {
|
||||
url += parametersFps;
|
||||
urlVideoParameters += parametersFps;
|
||||
}
|
||||
url = appendQueryParameter(url, urlVideoParameters);
|
||||
} else {
|
||||
if (hwCodec && MediaCodecVideoEncoder.isPlatformSupported()) {
|
||||
url += "&hd=true";
|
||||
url = appendQueryParameter(url, "hd=true");
|
||||
}
|
||||
}
|
||||
|
||||
// Get start bitrate.
|
||||
int startBitrate = 0;
|
||||
String bitrateTypeDefault = getString(R.string.pref_startbitrate_default);
|
||||
@ -305,30 +340,29 @@ public class ConnectActivity extends Activity {
|
||||
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";
|
||||
url = appendQueryParameter(url, "googCpuOveruseDetection=false");
|
||||
}
|
||||
// TODO(kjellander): Add support for custom parameters to the URL.
|
||||
connectToRoom(url, loopback, startBitrate, hwCodec);
|
||||
}
|
||||
};
|
||||
|
||||
private void connectToRoom(
|
||||
String roomUrl, boolean loopback, int startBitrate, boolean hwCodec) {
|
||||
if (validateUrl(roomUrl)) {
|
||||
Uri url = Uri.parse(roomUrl);
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
@ -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(
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
String roomId;
|
||||
String clientId;
|
||||
String roomUrl;
|
||||
String channelToken = "";
|
||||
String wssUrl = "";
|
||||
String wssPostUrl = "";
|
||||
boolean initiator;
|
||||
LinkedList<IceCandidate> 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<IceCandidate>();
|
||||
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 {
|
||||
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('?'));
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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;
|
||||
} else {
|
||||
initiator = roomJson.getInt("initiator") == 1;
|
||||
}
|
||||
Log.d(TAG, "Initiator: " + initiator);
|
||||
Log.d(TAG, "Room url: " + roomUrl);
|
||||
|
||||
LinkedList<PeerConnection.IceServer> 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
|
||||
|
@ -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)) {
|
||||
|
@ -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() {
|
||||
|
@ -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<GAEMessage> gaePostQueue;
|
||||
private LinkedList<PostMessage> postQueue;
|
||||
private String postMessageUrl;
|
||||
private String byeMessageUrl;
|
||||
|
||||
public WebSocketRTCClient(SignalingEvents events) {
|
||||
this.events = events;
|
||||
uiHandler = new Handler(Looper.getMainLooper());
|
||||
gaePostQueue = new LinkedList<GAEMessage>();
|
||||
postQueue = new LinkedList<PostMessage>();
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
@ -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,11 +132,9 @@ 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) {
|
||||
Log.d(TAG, "Websocket connection completed. Registering...");
|
||||
wsClient.register();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWebSocketMessage(final String msg) {
|
||||
@ -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")) {
|
||||
if (initiator) {
|
||||
SessionDescription sdp = new SessionDescription(
|
||||
SessionDescription.Type.fromCanonicalForm(type),
|
||||
(String)json.get("sdp"));
|
||||
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/<room>, 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) {
|
||||
if (loopback) {
|
||||
// In loopback mode rename this offer to answer and send 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());
|
||||
if (loopback) {
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
} else {
|
||||
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);
|
||||
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;
|
||||
}
|
||||
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<Void, Void, Void>() {
|
||||
public Void doInBackground(Void... unused) {
|
||||
@ -321,47 +340,72 @@ public class WebSocketRTCClient implements AppRTCClient,
|
||||
|
||||
// Send all queued messages if connected to the room.
|
||||
private void maybeDrainGAEPostQueue() {
|
||||
synchronized (gaePostQueue) {
|
||||
if (roomState != ConnectionState.CONNECTED) {
|
||||
return;
|
||||
}
|
||||
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;
|
||||
PostMessage postMessage = null;
|
||||
while (true) {
|
||||
synchronized (postQueue) {
|
||||
postMessage = postQueue.poll();
|
||||
}
|
||||
// Send POST request.
|
||||
if (postMessage == null) {
|
||||
break;
|
||||
}
|
||||
try {
|
||||
|
||||
// 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(gaeMessage.postUrl).openConnection();
|
||||
(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");
|
||||
connection.getOutputStream().write(
|
||||
gaeMessage.message.getBytes("UTF-8"));
|
||||
String replyHeader = connection.getHeaderField(null);
|
||||
if (!replyHeader.startsWith("HTTP/1.1 200 ")) {
|
||||
|
||||
// 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) {
|
||||
reportError("GAE POST error: " + e.getMessage());
|
||||
} 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() : "";
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user