Add new features to AppRTCDemo from private repo.
- Add HUD fragment with HUD related controls and more HUD statistics. - Create and set all peer connection constraints in PeerConnectionClient class. - Handle registration request in web socket class internally once web socket connection is opened. R=wzh@webrtc.org Review URL: https://webrtc-codereview.appspot.com/44669004 Cr-Commit-Position: refs/heads/master@{#8762} git-svn-id: http://webrtc.googlecode.com/svn/trunk@8762 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
parent
779c3d16b9
commit
2161234cf6
@ -4,8 +4,7 @@
|
|||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent">
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<android.opengl.GLSurfaceView
|
<android.opengl.GLSurfaceView
|
||||||
android:id="@+id/glview_call"
|
android:id="@+id/glview_call"
|
||||||
@ -16,5 +15,9 @@
|
|||||||
android:id="@+id/call_fragment_container"
|
android:id="@+id/call_fragment_container"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent" />
|
android:layout_height="match_parent" />
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/hud_fragment_container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent" />
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
@ -4,18 +4,7 @@
|
|||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent">
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/encoder_stat_call"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_alignParentEnd="true"
|
|
||||||
android:textStyle="bold"
|
|
||||||
android:textColor="#C000FF00"
|
|
||||||
android:textSize="12dp"
|
|
||||||
android:layout_margin="8dp"/>
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/contact_name_call"
|
android:id="@+id/contact_name_call"
|
||||||
@ -26,23 +15,6 @@
|
|||||||
android:textSize="24sp"
|
android:textSize="24sp"
|
||||||
android:layout_margin="8dp"/>
|
android:layout_margin="8dp"/>
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/hud_stat_call"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:alpha="0.4"
|
|
||||||
android:background="@android:color/white"
|
|
||||||
android:textColor="@android:color/black" />
|
|
||||||
|
|
||||||
<ImageButton
|
|
||||||
android:id="@+id/button_toggle_debug"
|
|
||||||
android:background="@android:drawable/ic_menu_info_details"
|
|
||||||
android:contentDescription="@string/toggle_debug"
|
|
||||||
android:layout_alignParentBottom="true"
|
|
||||||
android:layout_alignParentStart="true"
|
|
||||||
android:layout_width="48dp"
|
|
||||||
android:layout_height="48dp"/>
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/buttons_call_container"
|
android:id="@+id/buttons_call_container"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
|
75
talk/examples/android/res/layout/fragment_hud.xml
Normal file
75
talk/examples/android/res/layout/fragment_hud.xml
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/button_toggle_debug"
|
||||||
|
android:background="@android:drawable/ic_menu_info_details"
|
||||||
|
android:contentDescription="@string/toggle_debug"
|
||||||
|
android:layout_alignParentBottom="true"
|
||||||
|
android:layout_alignParentStart="true"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/encoder_stat_call"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textColor="#C000FF00"
|
||||||
|
android:textSize="12dp"
|
||||||
|
android:layout_margin="8dp"/>
|
||||||
|
|
||||||
|
<TableLayout
|
||||||
|
android:id="@+id/hudview_container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<TableRow>
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/hud_stat_bwe"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:alpha="0.4"
|
||||||
|
android:padding="2dip"
|
||||||
|
android:background="@android:color/white"
|
||||||
|
android:textColor="@android:color/black" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/hud_stat_connection"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:alpha="0.4"
|
||||||
|
android:padding="2dip"
|
||||||
|
android:background="@android:color/white"
|
||||||
|
android:textColor="@android:color/black" />
|
||||||
|
|
||||||
|
</TableRow>
|
||||||
|
|
||||||
|
<TableRow>
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/hud_stat_video_send"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:alpha="0.4"
|
||||||
|
android:padding="2dip"
|
||||||
|
android:background="@android:color/white"
|
||||||
|
android:textColor="@android:color/black" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/hud_stat_video_recv"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:padding="2dip"
|
||||||
|
android:alpha="0.4"
|
||||||
|
android:background="@android:color/white"
|
||||||
|
android:textColor="@android:color/black" />
|
||||||
|
</TableRow>
|
||||||
|
</TableLayout>
|
||||||
|
|
||||||
|
</RelativeLayout>
|
@ -43,7 +43,7 @@ import android.os.Bundle;
|
|||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.Window;
|
import android.view.Window;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager.LayoutParams;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import org.webrtc.IceCandidate;
|
import org.webrtc.IceCandidate;
|
||||||
@ -132,6 +132,7 @@ public class CallActivity extends Activity
|
|||||||
// Controls
|
// Controls
|
||||||
private GLSurfaceView videoView;
|
private GLSurfaceView videoView;
|
||||||
CallFragment callFragment;
|
CallFragment callFragment;
|
||||||
|
HudFragment hudFragment;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
@ -142,8 +143,12 @@ public class CallActivity extends Activity
|
|||||||
// Set window styles for fullscreen-window size. Needs to be done before
|
// Set window styles for fullscreen-window size. Needs to be done before
|
||||||
// adding content.
|
// adding content.
|
||||||
requestWindowFeature(Window.FEATURE_NO_TITLE);
|
requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
getWindow().addFlags(
|
||||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
LayoutParams.FLAG_FULLSCREEN
|
||||||
|
| LayoutParams.FLAG_KEEP_SCREEN_ON
|
||||||
|
| LayoutParams.FLAG_DISMISS_KEYGUARD
|
||||||
|
| LayoutParams.FLAG_SHOW_WHEN_LOCKED
|
||||||
|
| LayoutParams.FLAG_TURN_SCREEN_ON);
|
||||||
getWindow().getDecorView().setSystemUiVisibility(
|
getWindow().getDecorView().setSystemUiVisibility(
|
||||||
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
||||||
| View.SYSTEM_UI_FLAG_FULLSCREEN
|
| View.SYSTEM_UI_FLAG_FULLSCREEN
|
||||||
@ -157,6 +162,7 @@ public class CallActivity extends Activity
|
|||||||
// Create UI controls.
|
// Create UI controls.
|
||||||
videoView = (GLSurfaceView) findViewById(R.id.glview_call);
|
videoView = (GLSurfaceView) findViewById(R.id.glview_call);
|
||||||
callFragment = new CallFragment();
|
callFragment = new CallFragment();
|
||||||
|
hudFragment = new HudFragment();
|
||||||
|
|
||||||
// Create video renderers.
|
// Create video renderers.
|
||||||
VideoRendererGui.setView(videoView, new Runnable() {
|
VideoRendererGui.setView(videoView, new Runnable() {
|
||||||
@ -219,11 +225,14 @@ public class CallActivity extends Activity
|
|||||||
roomConnectionParameters = new RoomConnectionParameters(
|
roomConnectionParameters = new RoomConnectionParameters(
|
||||||
roomUri.toString(), roomId, loopback);
|
roomUri.toString(), roomId, loopback);
|
||||||
|
|
||||||
// Send intent arguments to fragment.
|
// Send intent arguments to fragments.
|
||||||
callFragment.setArguments(intent.getExtras());
|
callFragment.setArguments(intent.getExtras());
|
||||||
// Activate call fragment and start the call.
|
hudFragment.setArguments(intent.getExtras());
|
||||||
getFragmentManager().beginTransaction()
|
// Activate call and HUD fragments and start the call.
|
||||||
.add(R.id.call_fragment_container, callFragment).commit();
|
FragmentTransaction ft = getFragmentManager().beginTransaction();
|
||||||
|
ft.add(R.id.call_fragment_container, callFragment);
|
||||||
|
ft.add(R.id.hud_fragment_container, hudFragment);
|
||||||
|
ft.commit();
|
||||||
startCall();
|
startCall();
|
||||||
|
|
||||||
// For command line execution run connection for <runTimeMs> and exit.
|
// For command line execution run connection for <runTimeMs> and exit.
|
||||||
@ -296,8 +305,10 @@ public class CallActivity extends Activity
|
|||||||
FragmentTransaction ft = getFragmentManager().beginTransaction();
|
FragmentTransaction ft = getFragmentManager().beginTransaction();
|
||||||
if (callControlFragmentVisible) {
|
if (callControlFragmentVisible) {
|
||||||
ft.show(callFragment);
|
ft.show(callFragment);
|
||||||
|
ft.show(hudFragment);
|
||||||
} else {
|
} else {
|
||||||
ft.hide(callFragment);
|
ft.hide(callFragment);
|
||||||
|
ft.hide(hudFragment);
|
||||||
}
|
}
|
||||||
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
|
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
|
||||||
ft.commit();
|
ft.commit();
|
||||||
@ -387,6 +398,7 @@ public class CallActivity extends Activity
|
|||||||
|
|
||||||
// Disconnect from remote resources, dispose of local resources, and exit.
|
// Disconnect from remote resources, dispose of local resources, and exit.
|
||||||
private void disconnect() {
|
private void disconnect() {
|
||||||
|
activityRunning = false;
|
||||||
if (appRtcClient != null) {
|
if (appRtcClient != null) {
|
||||||
appRtcClient.disconnectFromRoom();
|
appRtcClient.disconnectFromRoom();
|
||||||
appRtcClient = null;
|
appRtcClient = null;
|
||||||
@ -436,6 +448,18 @@ public class CallActivity extends Activity
|
|||||||
logToast.show();
|
logToast.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void reportError(final String description) {
|
||||||
|
runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (!isError) {
|
||||||
|
isError = true;
|
||||||
|
disconnectWithErrorMessage(description);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// -----Implementation of AppRTCClient.AppRTCSignalingEvents ---------------
|
// -----Implementation of AppRTCClient.AppRTCSignalingEvents ---------------
|
||||||
// All callbacks are invoked from websocket signaling looper thread and
|
// All callbacks are invoked from websocket signaling looper thread and
|
||||||
// are routed to UI thread.
|
// are routed to UI thread.
|
||||||
@ -533,15 +557,7 @@ public class CallActivity extends Activity
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onChannelError(final String description) {
|
public void onChannelError(final String description) {
|
||||||
runOnUiThread(new Runnable() {
|
reportError(description);
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
if (!isError) {
|
|
||||||
isError = true;
|
|
||||||
disconnectWithErrorMessage(description);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----Implementation of PeerConnectionClient.PeerConnectionEvents.---------
|
// -----Implementation of PeerConnectionClient.PeerConnectionEvents.---------
|
||||||
@ -613,7 +629,7 @@ public class CallActivity extends Activity
|
|||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
if (!isError && iceConnected) {
|
if (!isError && iceConnected) {
|
||||||
callFragment.updateEncoderStatistics(reports);
|
hudFragment.updateEncoderStatistics(reports);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -621,14 +637,6 @@ public class CallActivity extends Activity
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPeerConnectionError(final String description) {
|
public void onPeerConnectionError(final String description) {
|
||||||
runOnUiThread(new Runnable() {
|
reportError(description);
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
if (!isError) {
|
|
||||||
isError = true;
|
|
||||||
disconnectWithErrorMessage(description);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,36 +30,26 @@ package org.appspot.apprtc;
|
|||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.Fragment;
|
import android.app.Fragment;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.util.TypedValue;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.ImageButton;
|
import android.widget.ImageButton;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import org.webrtc.StatsReport;
|
|
||||||
import org.webrtc.VideoRendererGui.ScalingType;
|
import org.webrtc.VideoRendererGui.ScalingType;
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fragment for call control.
|
* Fragment for call control.
|
||||||
*/
|
*/
|
||||||
public class CallFragment extends Fragment {
|
public class CallFragment extends Fragment {
|
||||||
private View controlView;
|
private View controlView;
|
||||||
private TextView encoderStatView;
|
private TextView contactView;
|
||||||
private TextView roomIdView;
|
|
||||||
private ImageButton disconnectButton;
|
private ImageButton disconnectButton;
|
||||||
private ImageButton cameraSwitchButton;
|
private ImageButton cameraSwitchButton;
|
||||||
private ImageButton videoScalingButton;
|
private ImageButton videoScalingButton;
|
||||||
private ImageButton toggleDebugButton;
|
|
||||||
private OnCallEvents callEvents;
|
private OnCallEvents callEvents;
|
||||||
private ScalingType scalingType;
|
private ScalingType scalingType;
|
||||||
private boolean displayHud;
|
private boolean videoCallEnabled = true;
|
||||||
private volatile boolean isRunning;
|
|
||||||
private TextView hudView;
|
|
||||||
private final CpuMonitor cpuMonitor = new CpuMonitor();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Call control interface for container activity.
|
* Call control interface for container activity.
|
||||||
@ -77,20 +67,14 @@ public class CallFragment extends Fragment {
|
|||||||
inflater.inflate(R.layout.fragment_call, container, false);
|
inflater.inflate(R.layout.fragment_call, container, false);
|
||||||
|
|
||||||
// Create UI controls.
|
// Create UI controls.
|
||||||
encoderStatView =
|
contactView =
|
||||||
(TextView) controlView.findViewById(R.id.encoder_stat_call);
|
|
||||||
roomIdView =
|
|
||||||
(TextView) controlView.findViewById(R.id.contact_name_call);
|
(TextView) controlView.findViewById(R.id.contact_name_call);
|
||||||
hudView =
|
|
||||||
(TextView) controlView.findViewById(R.id.hud_stat_call);
|
|
||||||
disconnectButton =
|
disconnectButton =
|
||||||
(ImageButton) controlView.findViewById(R.id.button_call_disconnect);
|
(ImageButton) controlView.findViewById(R.id.button_call_disconnect);
|
||||||
cameraSwitchButton =
|
cameraSwitchButton =
|
||||||
(ImageButton) controlView.findViewById(R.id.button_call_switch_camera);
|
(ImageButton) controlView.findViewById(R.id.button_call_switch_camera);
|
||||||
videoScalingButton =
|
videoScalingButton =
|
||||||
(ImageButton) controlView.findViewById(R.id.button_call_scaling_mode);
|
(ImageButton) controlView.findViewById(R.id.button_call_scaling_mode);
|
||||||
toggleDebugButton =
|
|
||||||
(ImageButton) controlView.findViewById(R.id.button_toggle_debug);
|
|
||||||
|
|
||||||
// Add buttons click events.
|
// Add buttons click events.
|
||||||
disconnectButton.setOnClickListener(new View.OnClickListener() {
|
disconnectButton.setOnClickListener(new View.OnClickListener() {
|
||||||
@ -124,17 +108,6 @@ public class CallFragment extends Fragment {
|
|||||||
});
|
});
|
||||||
scalingType = ScalingType.SCALE_ASPECT_FILL;
|
scalingType = ScalingType.SCALE_ASPECT_FILL;
|
||||||
|
|
||||||
toggleDebugButton.setOnClickListener(new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View view) {
|
|
||||||
if (displayHud) {
|
|
||||||
int visibility = (hudView.getVisibility() == View.VISIBLE)
|
|
||||||
? View.INVISIBLE : View.VISIBLE;
|
|
||||||
hudView.setVisibility(visibility);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return controlView;
|
return controlView;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,22 +117,13 @@ public class CallFragment extends Fragment {
|
|||||||
|
|
||||||
Bundle args = getArguments();
|
Bundle args = getArguments();
|
||||||
if (args != null) {
|
if (args != null) {
|
||||||
String roomId = args.getString(CallActivity.EXTRA_ROOMID);
|
String contactName = args.getString(CallActivity.EXTRA_ROOMID);
|
||||||
roomIdView.setText(roomId);
|
contactView.setText(contactName);
|
||||||
displayHud = args.getBoolean(CallActivity.EXTRA_DISPLAY_HUD, false);
|
videoCallEnabled = args.getBoolean(CallActivity.EXTRA_VIDEO_CALL, true);
|
||||||
|
}
|
||||||
|
if (!videoCallEnabled) {
|
||||||
|
cameraSwitchButton.setVisibility(View.INVISIBLE);
|
||||||
}
|
}
|
||||||
int visibility = displayHud ? View.VISIBLE : View.INVISIBLE;
|
|
||||||
encoderStatView.setVisibility(visibility);
|
|
||||||
toggleDebugButton.setVisibility(visibility);
|
|
||||||
hudView.setVisibility(View.INVISIBLE);
|
|
||||||
hudView.setTextSize(TypedValue.COMPLEX_UNIT_PT, 5);
|
|
||||||
isRunning = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onStop() {
|
|
||||||
isRunning = false;
|
|
||||||
super.onStop();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -168,73 +132,4 @@ public class CallFragment extends Fragment {
|
|||||||
callEvents = (OnCallEvents) activity;
|
callEvents = (OnCallEvents) activity;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, String> getReportMap(StatsReport report) {
|
|
||||||
Map<String, String> reportMap = new HashMap<String, String>();
|
|
||||||
for (StatsReport.Value value : report.values) {
|
|
||||||
reportMap.put(value.name, value.value);
|
|
||||||
}
|
|
||||||
return reportMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateEncoderStatistics(final StatsReport[] reports) {
|
|
||||||
if (!isRunning || !displayHud) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
String fps = null;
|
|
||||||
String targetBitrate = null;
|
|
||||||
String actualBitrate = null;
|
|
||||||
StringBuilder bweBuilder = new StringBuilder();
|
|
||||||
for (StatsReport report : reports) {
|
|
||||||
if (report.type.equals("ssrc") && report.id.contains("ssrc")
|
|
||||||
&& report.id.contains("send")) {
|
|
||||||
Map<String, String> reportMap = getReportMap(report);
|
|
||||||
String trackId = reportMap.get("googTrackId");
|
|
||||||
if (trackId != null
|
|
||||||
&& trackId.contains(PeerConnectionClient.VIDEO_TRACK_ID)) {
|
|
||||||
fps = reportMap.get("googFrameRateSent");
|
|
||||||
}
|
|
||||||
} else if (report.id.equals("bweforvideo")) {
|
|
||||||
Map<String, String> reportMap = getReportMap(report);
|
|
||||||
targetBitrate = reportMap.get("googTargetEncBitrate");
|
|
||||||
actualBitrate = reportMap.get("googActualEncBitrate");
|
|
||||||
|
|
||||||
for (StatsReport.Value value : report.values) {
|
|
||||||
String name = value.name.replace("goog", "")
|
|
||||||
.replace("Available", "").replace("Bandwidth", "")
|
|
||||||
.replace("Bitrate", "").replace("Enc", "");
|
|
||||||
bweBuilder.append(name).append("=").append(value.value)
|
|
||||||
.append(" ");
|
|
||||||
}
|
|
||||||
bweBuilder.append("\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StringBuilder stat = new StringBuilder(128);
|
|
||||||
if (fps != null) {
|
|
||||||
stat.append("Fps: ")
|
|
||||||
.append(fps)
|
|
||||||
.append("\n");
|
|
||||||
}
|
|
||||||
if (targetBitrate != null) {
|
|
||||||
stat.append("Target BR: ")
|
|
||||||
.append(targetBitrate)
|
|
||||||
.append("\n");
|
|
||||||
}
|
|
||||||
if (actualBitrate != null) {
|
|
||||||
stat.append("Actual BR: ")
|
|
||||||
.append(actualBitrate)
|
|
||||||
.append("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cpuMonitor.sampleCpuUtilization()) {
|
|
||||||
stat.append("CPU%: ")
|
|
||||||
.append(cpuMonitor.getCpuCurrent())
|
|
||||||
.append("/")
|
|
||||||
.append(cpuMonitor.getCpuAvg3())
|
|
||||||
.append("/")
|
|
||||||
.append(cpuMonitor.getCpuAvgAll());
|
|
||||||
}
|
|
||||||
encoderStatView.setText(stat.toString());
|
|
||||||
hudView.setText(bweBuilder.toString() + hudView.getText());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
217
talk/examples/android/src/org/appspot/apprtc/HudFragment.java
Normal file
217
talk/examples/android/src/org/appspot/apprtc/HudFragment.java
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
/*
|
||||||
|
* libjingle
|
||||||
|
* Copyright 2015 Google Inc.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
* 3. The name of the author may not be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||||
|
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||||
|
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||||
|
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||||
|
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||||
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.appspot.apprtc;
|
||||||
|
|
||||||
|
import android.app.Fragment;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.util.TypedValue;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.ImageButton;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import org.webrtc.StatsReport;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fragment for HUD statistics display.
|
||||||
|
*/
|
||||||
|
public class HudFragment extends Fragment {
|
||||||
|
private View controlView;
|
||||||
|
private TextView encoderStatView;
|
||||||
|
private TextView hudViewBwe;
|
||||||
|
private TextView hudViewConnection;
|
||||||
|
private TextView hudViewVideoSend;
|
||||||
|
private TextView hudViewVideoRecv;
|
||||||
|
private ImageButton toggleDebugButton;
|
||||||
|
private boolean videoCallEnabled;
|
||||||
|
private boolean displayHud;
|
||||||
|
private volatile boolean isRunning;
|
||||||
|
private final CpuMonitor cpuMonitor = new CpuMonitor();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||||
|
Bundle savedInstanceState) {
|
||||||
|
controlView = inflater.inflate(R.layout.fragment_hud, container, false);
|
||||||
|
|
||||||
|
// Create UI controls.
|
||||||
|
encoderStatView = (TextView) controlView.findViewById(R.id.encoder_stat_call);
|
||||||
|
hudViewBwe = (TextView) controlView.findViewById(R.id.hud_stat_bwe);
|
||||||
|
hudViewConnection = (TextView) controlView.findViewById(R.id.hud_stat_connection);
|
||||||
|
hudViewVideoSend = (TextView) controlView.findViewById(R.id.hud_stat_video_send);
|
||||||
|
hudViewVideoRecv = (TextView) controlView.findViewById(R.id.hud_stat_video_recv);
|
||||||
|
toggleDebugButton = (ImageButton) controlView.findViewById(R.id.button_toggle_debug);
|
||||||
|
|
||||||
|
toggleDebugButton.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
if (displayHud) {
|
||||||
|
int visibility = (hudViewBwe.getVisibility() == View.VISIBLE)
|
||||||
|
? View.INVISIBLE : View.VISIBLE;
|
||||||
|
hudViewsSetProperties(visibility);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return controlView;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStart() {
|
||||||
|
super.onStart();
|
||||||
|
|
||||||
|
Bundle args = getArguments();
|
||||||
|
if (args != null) {
|
||||||
|
videoCallEnabled = args.getBoolean(CallActivity.EXTRA_VIDEO_CALL, true);
|
||||||
|
displayHud = args.getBoolean(CallActivity.EXTRA_DISPLAY_HUD, false);
|
||||||
|
}
|
||||||
|
int visibility = displayHud ? View.VISIBLE : View.INVISIBLE;
|
||||||
|
encoderStatView.setVisibility(visibility);
|
||||||
|
toggleDebugButton.setVisibility(visibility);
|
||||||
|
hudViewsSetProperties(View.INVISIBLE);
|
||||||
|
isRunning = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStop() {
|
||||||
|
isRunning = false;
|
||||||
|
super.onStop();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void hudViewsSetProperties(int visibility) {
|
||||||
|
hudViewBwe.setVisibility(visibility);
|
||||||
|
hudViewConnection.setVisibility(visibility);
|
||||||
|
hudViewVideoSend.setVisibility(visibility);
|
||||||
|
hudViewVideoRecv.setVisibility(visibility);
|
||||||
|
hudViewBwe.setTextSize(TypedValue.COMPLEX_UNIT_PT, 5);
|
||||||
|
hudViewConnection.setTextSize(TypedValue.COMPLEX_UNIT_PT, 5);
|
||||||
|
hudViewVideoSend.setTextSize(TypedValue.COMPLEX_UNIT_PT, 5);
|
||||||
|
hudViewVideoRecv.setTextSize(TypedValue.COMPLEX_UNIT_PT, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, String> getReportMap(StatsReport report) {
|
||||||
|
Map<String, String> reportMap = new HashMap<String, String>();
|
||||||
|
for (StatsReport.Value value : report.values) {
|
||||||
|
reportMap.put(value.name, value.value);
|
||||||
|
}
|
||||||
|
return reportMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateEncoderStatistics(final StatsReport[] reports) {
|
||||||
|
if (!isRunning || !displayHud) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
StringBuilder encoderStat = new StringBuilder(128);
|
||||||
|
StringBuilder bweStat = new StringBuilder();
|
||||||
|
StringBuilder connectionStat = new StringBuilder();
|
||||||
|
StringBuilder videoSendStat = new StringBuilder();
|
||||||
|
StringBuilder videoRecvStat = new StringBuilder();
|
||||||
|
String fps = null;
|
||||||
|
String targetBitrate = null;
|
||||||
|
String actualBitrate = null;
|
||||||
|
|
||||||
|
for (StatsReport report : reports) {
|
||||||
|
if (report.type.equals("ssrc") && report.id.contains("ssrc")
|
||||||
|
&& report.id.contains("send")) {
|
||||||
|
// Send video statistics.
|
||||||
|
Map<String, String> reportMap = getReportMap(report);
|
||||||
|
String trackId = reportMap.get("googTrackId");
|
||||||
|
if (trackId != null && trackId.contains(PeerConnectionClient.VIDEO_TRACK_ID)) {
|
||||||
|
fps = reportMap.get("googFrameRateSent");
|
||||||
|
videoSendStat.append(report.id).append("\n");
|
||||||
|
for (StatsReport.Value value : report.values) {
|
||||||
|
String name = value.name.replace("goog", "");
|
||||||
|
videoSendStat.append(name).append("=").append(value.value).append("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (report.type.equals("ssrc") && report.id.contains("ssrc")
|
||||||
|
&& report.id.contains("recv")) {
|
||||||
|
// Receive video statistics.
|
||||||
|
Map<String, String> reportMap = getReportMap(report);
|
||||||
|
// Check if this stat is for video track.
|
||||||
|
String frameWidth = reportMap.get("googFrameWidthReceived");
|
||||||
|
if (frameWidth != null) {
|
||||||
|
videoRecvStat.append(report.id).append("\n");
|
||||||
|
for (StatsReport.Value value : report.values) {
|
||||||
|
String name = value.name.replace("goog", "");
|
||||||
|
videoRecvStat.append(name).append("=").append(value.value).append("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (report.id.equals("bweforvideo")) {
|
||||||
|
// BWE statistics.
|
||||||
|
Map<String, String> reportMap = getReportMap(report);
|
||||||
|
targetBitrate = reportMap.get("googTargetEncBitrate");
|
||||||
|
actualBitrate = reportMap.get("googActualEncBitrate");
|
||||||
|
|
||||||
|
bweStat.append(report.id).append("\n");
|
||||||
|
for (StatsReport.Value value : report.values) {
|
||||||
|
String name = value.name.replace("goog", "").replace("Available", "");
|
||||||
|
bweStat.append(name).append("=").append(value.value).append("\n");
|
||||||
|
}
|
||||||
|
} else if (report.type.equals("googCandidatePair")) {
|
||||||
|
// Connection statistics.
|
||||||
|
Map<String, String> reportMap = getReportMap(report);
|
||||||
|
String activeConnection = reportMap.get("googActiveConnection");
|
||||||
|
if (activeConnection != null && activeConnection.equals("true")) {
|
||||||
|
connectionStat.append(report.id).append("\n");
|
||||||
|
for (StatsReport.Value value : report.values) {
|
||||||
|
String name = value.name.replace("goog", "");
|
||||||
|
connectionStat.append(name).append("=").append(value.value).append("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hudViewBwe.setText(bweStat.toString());
|
||||||
|
hudViewConnection.setText(connectionStat.toString());
|
||||||
|
hudViewVideoSend.setText(videoSendStat.toString());
|
||||||
|
hudViewVideoRecv.setText(videoRecvStat.toString());
|
||||||
|
|
||||||
|
if (videoCallEnabled) {
|
||||||
|
if (fps != null) {
|
||||||
|
encoderStat.append("Fps: ").append(fps).append("\n");
|
||||||
|
}
|
||||||
|
if (targetBitrate != null) {
|
||||||
|
encoderStat.append("Target BR: ").append(targetBitrate).append("\n");
|
||||||
|
}
|
||||||
|
if (actualBitrate != null) {
|
||||||
|
encoderStat.append("Actual BR: ").append(actualBitrate).append("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cpuMonitor.sampleCpuUtilization()) {
|
||||||
|
encoderStat.append("CPU%: ")
|
||||||
|
.append(cpuMonitor.getCpuCurrent()).append("/")
|
||||||
|
.append(cpuMonitor.getCpuAvg3()).append("/")
|
||||||
|
.append(cpuMonitor.getCpuAvgAll());
|
||||||
|
}
|
||||||
|
encoderStatView.setText(encoderStat.toString());
|
||||||
|
}
|
||||||
|
}
|
@ -82,6 +82,7 @@ public class PeerConnectionClient {
|
|||||||
private static final String MIN_VIDEO_HEIGHT_CONSTRAINT = "minHeight";
|
private static final String MIN_VIDEO_HEIGHT_CONSTRAINT = "minHeight";
|
||||||
private static final String MAX_VIDEO_FPS_CONSTRAINT = "maxFrameRate";
|
private static final String MAX_VIDEO_FPS_CONSTRAINT = "maxFrameRate";
|
||||||
private static final String MIN_VIDEO_FPS_CONSTRAINT = "minFrameRate";
|
private static final String MIN_VIDEO_FPS_CONSTRAINT = "minFrameRate";
|
||||||
|
private static final String DTLS_SRTP_KEY_AGREEMENT_CONSTRAINT = "DtlsSrtpKeyAgreement";
|
||||||
private static final int HD_VIDEO_WIDTH = 1280;
|
private static final int HD_VIDEO_WIDTH = 1280;
|
||||||
private static final int HD_VIDEO_HEIGHT = 720;
|
private static final int HD_VIDEO_HEIGHT = 720;
|
||||||
private static final int MAX_VIDEO_WIDTH = 1280;
|
private static final int MAX_VIDEO_WIDTH = 1280;
|
||||||
@ -103,13 +104,15 @@ public class PeerConnectionClient {
|
|||||||
private VideoRenderer.Callbacks localRender;
|
private VideoRenderer.Callbacks localRender;
|
||||||
private VideoRenderer.Callbacks remoteRender;
|
private VideoRenderer.Callbacks remoteRender;
|
||||||
private SignalingParameters signalingParameters;
|
private SignalingParameters signalingParameters;
|
||||||
|
private MediaConstraints pcConstraints;
|
||||||
private MediaConstraints videoConstraints;
|
private MediaConstraints videoConstraints;
|
||||||
|
private MediaConstraints audioConstraints;
|
||||||
|
private MediaConstraints sdpMediaConstraints;
|
||||||
private PeerConnectionParameters peerConnectionParameters;
|
private PeerConnectionParameters peerConnectionParameters;
|
||||||
// Queued remote ICE candidates are consumed only after both local and
|
// Queued remote ICE candidates are consumed only after both local and
|
||||||
// remote descriptions are set. Similarly local ICE candidates are sent to
|
// remote descriptions are set. Similarly local ICE candidates are sent to
|
||||||
// remote peer after both local and remote description are set.
|
// remote peer after both local and remote description are set.
|
||||||
private LinkedList<IceCandidate> queuedRemoteCandidates = null;
|
private LinkedList<IceCandidate> queuedRemoteCandidates = null;
|
||||||
private MediaConstraints sdpMediaConstraints;
|
|
||||||
private PeerConnectionEvents events;
|
private PeerConnectionEvents events;
|
||||||
private boolean isInitiator;
|
private boolean isInitiator;
|
||||||
private SessionDescription localSdp = null; // either offer or answer SDP
|
private SessionDescription localSdp = null; // either offer or answer SDP
|
||||||
@ -210,6 +213,7 @@ public class PeerConnectionClient {
|
|||||||
final PeerConnectionEvents events) {
|
final PeerConnectionEvents events) {
|
||||||
this.peerConnectionParameters = peerConnectionParameters;
|
this.peerConnectionParameters = peerConnectionParameters;
|
||||||
this.events = events;
|
this.events = events;
|
||||||
|
videoCallEnabled = peerConnectionParameters.videoCallEnabled;
|
||||||
executor.requestStart();
|
executor.requestStart();
|
||||||
executor.execute(new Runnable() {
|
executor.execute(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
@ -230,59 +234,10 @@ public class PeerConnectionClient {
|
|||||||
this.localRender = localRender;
|
this.localRender = localRender;
|
||||||
this.remoteRender = remoteRender;
|
this.remoteRender = remoteRender;
|
||||||
this.signalingParameters = signalingParameters;
|
this.signalingParameters = signalingParameters;
|
||||||
// Merge video constraints from signaling parameters and peer connection
|
|
||||||
// parameters.
|
|
||||||
videoConstraints = signalingParameters.videoConstraints;
|
|
||||||
if (signalingParameters.videoConstraints == null) {
|
|
||||||
videoCallEnabled = false;
|
|
||||||
}
|
|
||||||
// Check if there is a camera on device and disable video call if not.
|
|
||||||
numberOfCameras = VideoCapturerAndroid.getDeviceCount();
|
|
||||||
if (numberOfCameras == 0) {
|
|
||||||
Log.w(TAG, "No camera on device. Switch to audio only call.");
|
|
||||||
videoCallEnabled = false;
|
|
||||||
}
|
|
||||||
if (videoCallEnabled) {
|
|
||||||
int videoWidth = peerConnectionParameters.videoWidth;
|
|
||||||
int videoHeight = peerConnectionParameters.videoHeight;
|
|
||||||
|
|
||||||
// If HW video encoder is supported and video resolution is not
|
|
||||||
// specified force it to HD.
|
|
||||||
if ((videoWidth == 0 || videoHeight == 0)
|
|
||||||
&& peerConnectionParameters.videoCodecHwAcceleration
|
|
||||||
&& MediaCodecVideoEncoder.isVp8HwSupported()) {
|
|
||||||
videoWidth = HD_VIDEO_WIDTH;
|
|
||||||
videoHeight = HD_VIDEO_HEIGHT;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add video resolution constraints.
|
|
||||||
if (videoWidth > 0 && videoHeight > 0) {
|
|
||||||
videoWidth = Math.min(videoWidth, MAX_VIDEO_WIDTH);
|
|
||||||
videoHeight = Math.min(videoHeight, MAX_VIDEO_HEIGHT);
|
|
||||||
videoConstraints.mandatory.add(new KeyValuePair(
|
|
||||||
MIN_VIDEO_WIDTH_CONSTRAINT, Integer.toString(videoWidth)));
|
|
||||||
videoConstraints.mandatory.add(new KeyValuePair(
|
|
||||||
MAX_VIDEO_WIDTH_CONSTRAINT, Integer.toString(videoWidth)));
|
|
||||||
videoConstraints.mandatory.add(new KeyValuePair(
|
|
||||||
MIN_VIDEO_HEIGHT_CONSTRAINT, Integer.toString(videoHeight)));
|
|
||||||
videoConstraints.mandatory.add(new KeyValuePair(
|
|
||||||
MAX_VIDEO_HEIGHT_CONSTRAINT, Integer.toString(videoHeight)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add fps constraints.
|
|
||||||
int videoFps = peerConnectionParameters.videoFps;
|
|
||||||
if (videoFps > 0) {
|
|
||||||
videoFps = Math.min(videoFps, MAX_VIDEO_FPS);
|
|
||||||
videoConstraints.mandatory.add(new KeyValuePair(
|
|
||||||
MIN_VIDEO_FPS_CONSTRAINT, Integer.toString(videoFps)));
|
|
||||||
videoConstraints.mandatory.add(new KeyValuePair(
|
|
||||||
MAX_VIDEO_FPS_CONSTRAINT, Integer.toString(videoFps)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
executor.execute(new Runnable() {
|
executor.execute(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
createMediaConstraintsInternal();
|
||||||
createPeerConnectionInternal();
|
createPeerConnectionInternal();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -303,7 +258,6 @@ public class PeerConnectionClient {
|
|||||||
Log.d(TAG, "Create peer connection factory with EGLContext "
|
Log.d(TAG, "Create peer connection factory with EGLContext "
|
||||||
+ renderEGLContext + ". Use video: "
|
+ renderEGLContext + ". Use video: "
|
||||||
+ peerConnectionParameters.videoCallEnabled);
|
+ peerConnectionParameters.videoCallEnabled);
|
||||||
videoCallEnabled = peerConnectionParameters.videoCallEnabled;
|
|
||||||
isError = false;
|
isError = false;
|
||||||
// Check if VP9 is used by default.
|
// Check if VP9 is used by default.
|
||||||
if (videoCallEnabled && peerConnectionParameters.videoCodec != null
|
if (videoCallEnabled && peerConnectionParameters.videoCodec != null
|
||||||
@ -340,18 +294,68 @@ public class PeerConnectionClient {
|
|||||||
protected void configureFactory(PeerConnectionFactory factory) {
|
protected void configureFactory(PeerConnectionFactory factory) {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createPeerConnectionInternal() {
|
private void createMediaConstraintsInternal() {
|
||||||
if (factory == null || isError) {
|
// Create peer connection constraints.
|
||||||
Log.e(TAG, "Peerconnection factory is not created");
|
pcConstraints = new MediaConstraints();
|
||||||
return;
|
// Enable DTLS for normal calls and disable for loopback calls.
|
||||||
|
if (peerConnectionParameters.loopback) {
|
||||||
|
pcConstraints.optional.add(
|
||||||
|
new MediaConstraints.KeyValuePair(DTLS_SRTP_KEY_AGREEMENT_CONSTRAINT, "false"));
|
||||||
|
} else {
|
||||||
|
pcConstraints.optional.add(
|
||||||
|
new MediaConstraints.KeyValuePair(DTLS_SRTP_KEY_AGREEMENT_CONSTRAINT, "true"));
|
||||||
}
|
}
|
||||||
Log.d(TAG, "Create peer connection");
|
|
||||||
if (videoConstraints != null) {
|
|
||||||
Log.d(TAG, "VideoConstraints: " + videoConstraints.toString());
|
|
||||||
}
|
|
||||||
isInitiator = signalingParameters.initiator;
|
|
||||||
queuedRemoteCandidates = new LinkedList<IceCandidate>();
|
|
||||||
|
|
||||||
|
// Check if there is a camera on device and disable video call if not.
|
||||||
|
numberOfCameras = VideoCapturerAndroid.getDeviceCount();
|
||||||
|
if (numberOfCameras == 0) {
|
||||||
|
Log.w(TAG, "No camera on device. Switch to audio only call.");
|
||||||
|
videoCallEnabled = false;
|
||||||
|
}
|
||||||
|
// Create video constraints if video call is enabled.
|
||||||
|
if (videoCallEnabled) {
|
||||||
|
videoConstraints = new MediaConstraints();
|
||||||
|
int videoWidth = peerConnectionParameters.videoWidth;
|
||||||
|
int videoHeight = peerConnectionParameters.videoHeight;
|
||||||
|
|
||||||
|
// If VP8 HW video encoder is supported and video resolution is not
|
||||||
|
// specified force it to HD.
|
||||||
|
if ((videoWidth == 0 || videoHeight == 0)
|
||||||
|
&& peerConnectionParameters.videoCodecHwAcceleration
|
||||||
|
&& MediaCodecVideoEncoder.isVp8HwSupported()) {
|
||||||
|
videoWidth = HD_VIDEO_WIDTH;
|
||||||
|
videoHeight = HD_VIDEO_HEIGHT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add video resolution constraints.
|
||||||
|
if (videoWidth > 0 && videoHeight > 0) {
|
||||||
|
videoWidth = Math.min(videoWidth, MAX_VIDEO_WIDTH);
|
||||||
|
videoHeight = Math.min(videoHeight, MAX_VIDEO_HEIGHT);
|
||||||
|
videoConstraints.mandatory.add(new KeyValuePair(
|
||||||
|
MIN_VIDEO_WIDTH_CONSTRAINT, Integer.toString(videoWidth)));
|
||||||
|
videoConstraints.mandatory.add(new KeyValuePair(
|
||||||
|
MAX_VIDEO_WIDTH_CONSTRAINT, Integer.toString(videoWidth)));
|
||||||
|
videoConstraints.mandatory.add(new KeyValuePair(
|
||||||
|
MIN_VIDEO_HEIGHT_CONSTRAINT, Integer.toString(videoHeight)));
|
||||||
|
videoConstraints.mandatory.add(new KeyValuePair(
|
||||||
|
MAX_VIDEO_HEIGHT_CONSTRAINT, Integer.toString(videoHeight)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add fps constraints.
|
||||||
|
int videoFps = peerConnectionParameters.videoFps;
|
||||||
|
if (videoFps > 0) {
|
||||||
|
videoFps = Math.min(videoFps, MAX_VIDEO_FPS);
|
||||||
|
videoConstraints.mandatory.add(new KeyValuePair(
|
||||||
|
MIN_VIDEO_FPS_CONSTRAINT, Integer.toString(videoFps)));
|
||||||
|
videoConstraints.mandatory.add(new KeyValuePair(
|
||||||
|
MAX_VIDEO_FPS_CONSTRAINT, Integer.toString(videoFps)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create audio constraints.
|
||||||
|
audioConstraints = new MediaConstraints();
|
||||||
|
|
||||||
|
// Create SDP constraints.
|
||||||
sdpMediaConstraints = new MediaConstraints();
|
sdpMediaConstraints = new MediaConstraints();
|
||||||
sdpMediaConstraints.mandatory.add(new MediaConstraints.KeyValuePair(
|
sdpMediaConstraints.mandatory.add(new MediaConstraints.KeyValuePair(
|
||||||
"OfferToReceiveAudio", "true"));
|
"OfferToReceiveAudio", "true"));
|
||||||
@ -362,10 +366,20 @@ public class PeerConnectionClient {
|
|||||||
sdpMediaConstraints.mandatory.add(new MediaConstraints.KeyValuePair(
|
sdpMediaConstraints.mandatory.add(new MediaConstraints.KeyValuePair(
|
||||||
"OfferToReceiveVideo", "false"));
|
"OfferToReceiveVideo", "false"));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createPeerConnectionInternal() {
|
||||||
|
if (factory == null || isError) {
|
||||||
|
Log.e(TAG, "Peerconnection factory is not created");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Log.d(TAG, "Create peer connection");
|
||||||
|
Log.d(TAG, "PCConstraints: " + pcConstraints.toString());
|
||||||
|
if (videoConstraints != null) {
|
||||||
|
Log.d(TAG, "VideoConstraints: " + videoConstraints.toString());
|
||||||
|
}
|
||||||
|
queuedRemoteCandidates = new LinkedList<IceCandidate>();
|
||||||
|
|
||||||
MediaConstraints pcConstraints = signalingParameters.pcConstraints;
|
|
||||||
pcConstraints.optional.add(
|
|
||||||
new MediaConstraints.KeyValuePair("RtpDataChannels", "true"));
|
|
||||||
peerConnection = factory.createPeerConnection(
|
peerConnection = factory.createPeerConnection(
|
||||||
signalingParameters.iceServers, pcConstraints, pcObserver);
|
signalingParameters.iceServers, pcConstraints, pcObserver);
|
||||||
isInitiator = false;
|
isInitiator = false;
|
||||||
@ -390,11 +404,9 @@ public class PeerConnectionClient {
|
|||||||
mediaStream.addTrack(createVideoTrack(videoCapturer));
|
mediaStream.addTrack(createVideoTrack(videoCapturer));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (signalingParameters.audioConstraints != null) {
|
mediaStream.addTrack(factory.createAudioTrack(
|
||||||
mediaStream.addTrack(factory.createAudioTrack(
|
AUDIO_TRACK_ID,
|
||||||
AUDIO_TRACK_ID,
|
factory.createAudioSource(audioConstraints)));
|
||||||
factory.createAudioSource(signalingParameters.audioConstraints)));
|
|
||||||
}
|
|
||||||
peerConnection.addStream(mediaStream);
|
peerConnection.addStream(mediaStream);
|
||||||
|
|
||||||
Log.d(TAG, "Peer connection created.");
|
Log.d(TAG, "Peer connection created.");
|
||||||
@ -611,11 +623,9 @@ public class PeerConnectionClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private VideoTrack createVideoTrack(VideoCapturerAndroid capturer) {
|
private VideoTrack createVideoTrack(VideoCapturerAndroid capturer) {
|
||||||
videoSource = factory.createVideoSource(
|
videoSource = factory.createVideoSource(capturer, videoConstraints);
|
||||||
capturer, signalingParameters.videoConstraints);
|
|
||||||
|
|
||||||
localVideoTrack =
|
localVideoTrack = factory.createVideoTrack(VIDEO_TRACK_ID, videoSource);
|
||||||
factory.createVideoTrack(VIDEO_TRACK_ID, videoSource);
|
|
||||||
localVideoTrack.setEnabled(renderVideo);
|
localVideoTrack.setEnabled(renderVideo);
|
||||||
localVideoTrack.addRenderer(new VideoRenderer(localRender));
|
localVideoTrack.addRenderer(new VideoRenderer(localRender));
|
||||||
return localVideoTrack;
|
return localVideoTrack;
|
||||||
@ -700,8 +710,8 @@ public class PeerConnectionClient {
|
|||||||
if (isAudio) {
|
if (isAudio) {
|
||||||
mediaDescription = "m=audio ";
|
mediaDescription = "m=audio ";
|
||||||
}
|
}
|
||||||
for (int i = 0; (i < lines.length) &&
|
for (int i = 0; (i < lines.length)
|
||||||
(mLineIndex == -1 || codecRtpMap == null); i++) {
|
&& (mLineIndex == -1 || codecRtpMap == null); i++) {
|
||||||
if (lines[i].startsWith(mediaDescription)) {
|
if (lines[i].startsWith(mediaDescription)) {
|
||||||
mLineIndex = i;
|
mLineIndex = i;
|
||||||
continue;
|
continue;
|
||||||
@ -720,23 +730,27 @@ public class PeerConnectionClient {
|
|||||||
Log.w(TAG, "No rtpmap for " + codec);
|
Log.w(TAG, "No rtpmap for " + codec);
|
||||||
return sdpDescription;
|
return sdpDescription;
|
||||||
}
|
}
|
||||||
Log.d(TAG, "Found " + codec + " rtpmap " + codecRtpMap + ", prefer at " +
|
Log.d(TAG, "Found " + codec + " rtpmap " + codecRtpMap + ", prefer at "
|
||||||
lines[mLineIndex]);
|
+ lines[mLineIndex]);
|
||||||
String[] origMLineParts = lines[mLineIndex].split(" ");
|
String[] origMLineParts = lines[mLineIndex].split(" ");
|
||||||
StringBuilder newMLine = new StringBuilder();
|
if (origMLineParts.length > 3) {
|
||||||
int origPartIndex = 0;
|
StringBuilder newMLine = new StringBuilder();
|
||||||
// Format is: m=<media> <port> <proto> <fmt> ...
|
int origPartIndex = 0;
|
||||||
newMLine.append(origMLineParts[origPartIndex++]).append(" ");
|
// Format is: m=<media> <port> <proto> <fmt> ...
|
||||||
newMLine.append(origMLineParts[origPartIndex++]).append(" ");
|
newMLine.append(origMLineParts[origPartIndex++]).append(" ");
|
||||||
newMLine.append(origMLineParts[origPartIndex++]).append(" ");
|
newMLine.append(origMLineParts[origPartIndex++]).append(" ");
|
||||||
newMLine.append(codecRtpMap);
|
newMLine.append(origMLineParts[origPartIndex++]).append(" ");
|
||||||
for (; origPartIndex < origMLineParts.length; origPartIndex++) {
|
newMLine.append(codecRtpMap);
|
||||||
if (!origMLineParts[origPartIndex].equals(codecRtpMap)) {
|
for (; origPartIndex < origMLineParts.length; origPartIndex++) {
|
||||||
newMLine.append(" ").append(origMLineParts[origPartIndex]);
|
if (!origMLineParts[origPartIndex].equals(codecRtpMap)) {
|
||||||
|
newMLine.append(" ").append(origMLineParts[origPartIndex]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
lines[mLineIndex] = newMLine.toString();
|
||||||
|
Log.d(TAG, "Change media description: " + lines[mLineIndex]);
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, "Wrong SDP media description format: " + lines[mLineIndex]);
|
||||||
}
|
}
|
||||||
lines[mLineIndex] = newMLine.toString();
|
|
||||||
Log.d(TAG, "Change media description: " + lines[mLineIndex]);
|
|
||||||
StringBuilder newSdpDescription = new StringBuilder();
|
StringBuilder newSdpDescription = new StringBuilder();
|
||||||
for (String line : lines) {
|
for (String line : lines) {
|
||||||
newSdpDescription.append(line).append("\r\n");
|
newSdpDescription.append(line).append("\r\n");
|
||||||
|
@ -43,8 +43,8 @@ import org.webrtc.SessionDescription;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.net.URLConnection;
|
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.Scanner;
|
import java.util.Scanner;
|
||||||
|
|
||||||
@ -54,10 +54,10 @@ import java.util.Scanner;
|
|||||||
*/
|
*/
|
||||||
public class RoomParametersFetcher {
|
public class RoomParametersFetcher {
|
||||||
private static final String TAG = "RoomRTCClient";
|
private static final String TAG = "RoomRTCClient";
|
||||||
|
private static final int TURN_HTTP_TIMEOUT_MS = 5000;
|
||||||
private final RoomParametersFetcherEvents events;
|
private final RoomParametersFetcherEvents events;
|
||||||
private final boolean loopback;
|
private final String roomUrl;
|
||||||
private final String registerUrl;
|
private final String roomMessage;
|
||||||
private final String registerMessage;
|
|
||||||
private AsyncHttpURLConnection httpConnection;
|
private AsyncHttpURLConnection httpConnection;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -76,18 +76,17 @@ public class RoomParametersFetcher {
|
|||||||
public void onSignalingParametersError(final String description);
|
public void onSignalingParametersError(final String description);
|
||||||
}
|
}
|
||||||
|
|
||||||
public RoomParametersFetcher(boolean loopback, String registerUrl,
|
public RoomParametersFetcher(String roomUrl, String roomMessage,
|
||||||
String registerMessage, final RoomParametersFetcherEvents events) {
|
final RoomParametersFetcherEvents events) {
|
||||||
this.loopback = loopback;
|
this.roomUrl = roomUrl;
|
||||||
this.registerUrl = registerUrl;
|
this.roomMessage = roomMessage;
|
||||||
this.registerMessage = registerMessage;
|
|
||||||
this.events = events;
|
this.events = events;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void makeRequest() {
|
public void makeRequest() {
|
||||||
Log.d(TAG, "Connecting to room: " + registerUrl);
|
Log.d(TAG, "Connecting to room: " + roomUrl);
|
||||||
httpConnection = new AsyncHttpURLConnection(
|
httpConnection = new AsyncHttpURLConnection(
|
||||||
"POST", registerUrl, registerMessage,
|
"POST", roomUrl, roomMessage,
|
||||||
new AsyncHttpEvents() {
|
new AsyncHttpEvents() {
|
||||||
@Override
|
@Override
|
||||||
public void onHttpError(String errorMessage) {
|
public void onHttpError(String errorMessage) {
|
||||||
@ -161,6 +160,7 @@ public class RoomParametersFetcher {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Request TURN servers.
|
||||||
if (!isTurnPresent) {
|
if (!isTurnPresent) {
|
||||||
LinkedList<PeerConnection.IceServer> turnServers =
|
LinkedList<PeerConnection.IceServer> turnServers =
|
||||||
requestTurnServers(roomJson.getString("turn_url"));
|
requestTurnServers(roomJson.getString("turn_url"));
|
||||||
@ -170,17 +170,13 @@ public class RoomParametersFetcher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MediaConstraints pcConstraints = constraintsFromJSON(
|
MediaConstraints pcConstraints = constraintsFromJSON(roomJson.getString("pc_constraints"));
|
||||||
roomJson.getString("pc_constraints"));
|
|
||||||
addDTLSConstraintIfMissing(pcConstraints, loopback);
|
|
||||||
Log.d(TAG, "pcConstraints: " + pcConstraints);
|
Log.d(TAG, "pcConstraints: " + pcConstraints);
|
||||||
MediaConstraints videoConstraints = constraintsFromJSON(
|
MediaConstraints videoConstraints = constraintsFromJSON(
|
||||||
getAVConstraints("video",
|
getAVConstraints("video", roomJson.getString("media_constraints")));
|
||||||
roomJson.getString("media_constraints")));
|
|
||||||
Log.d(TAG, "videoConstraints: " + videoConstraints);
|
Log.d(TAG, "videoConstraints: " + videoConstraints);
|
||||||
MediaConstraints audioConstraints = constraintsFromJSON(
|
MediaConstraints audioConstraints = constraintsFromJSON(
|
||||||
getAVConstraints("audio",
|
getAVConstraints("audio", roomJson.getString("media_constraints")));
|
||||||
roomJson.getString("media_constraints")));
|
|
||||||
Log.d(TAG, "audioConstraints: " + audioConstraints);
|
Log.d(TAG, "audioConstraints: " + audioConstraints);
|
||||||
|
|
||||||
SignalingParameters params = new SignalingParameters(
|
SignalingParameters params = new SignalingParameters(
|
||||||
@ -197,35 +193,10 @@ public class RoomParametersFetcher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mimic Chrome and set DtlsSrtpKeyAgreement to true if not set to false by
|
|
||||||
// the web-app.
|
|
||||||
private void addDTLSConstraintIfMissing(
|
|
||||||
MediaConstraints pcConstraints, boolean loopback) {
|
|
||||||
for (MediaConstraints.KeyValuePair pair : pcConstraints.mandatory) {
|
|
||||||
if (pair.getKey().equals("DtlsSrtpKeyAgreement")) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (MediaConstraints.KeyValuePair pair : pcConstraints.optional) {
|
|
||||||
if (pair.getKey().equals("DtlsSrtpKeyAgreement")) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// DTLS isn't being specified (e.g. for debug=loopback calls), so enable
|
|
||||||
// it for normal calls and disable for loopback calls.
|
|
||||||
if (loopback) {
|
|
||||||
pcConstraints.optional.add(
|
|
||||||
new MediaConstraints.KeyValuePair("DtlsSrtpKeyAgreement", "false"));
|
|
||||||
} else {
|
|
||||||
pcConstraints.optional.add(
|
|
||||||
new MediaConstraints.KeyValuePair("DtlsSrtpKeyAgreement", "true"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the constraints specified for |type| of "audio" or "video" in
|
// Return the constraints specified for |type| of "audio" or "video" in
|
||||||
// |mediaConstraintsString|.
|
// |mediaConstraintsString|.
|
||||||
private String getAVConstraints (
|
private String getAVConstraints (
|
||||||
String type, String mediaConstraintsString) throws JSONException {
|
String type, String mediaConstraintsString) throws JSONException {
|
||||||
JSONObject json = new JSONObject(mediaConstraintsString);
|
JSONObject json = new JSONObject(mediaConstraintsString);
|
||||||
// Tricky handling of values that are allowed to be (boolean or
|
// Tricky handling of values that are allowed to be (boolean or
|
||||||
// MediaTrackConstraints) by the getUserMedia() spec. There are three
|
// MediaTrackConstraints) by the getUserMedia() spec. There are three
|
||||||
@ -282,10 +253,17 @@ public class RoomParametersFetcher {
|
|||||||
LinkedList<PeerConnection.IceServer> turnServers =
|
LinkedList<PeerConnection.IceServer> turnServers =
|
||||||
new LinkedList<PeerConnection.IceServer>();
|
new LinkedList<PeerConnection.IceServer>();
|
||||||
Log.d(TAG, "Request TURN from: " + url);
|
Log.d(TAG, "Request TURN from: " + url);
|
||||||
URLConnection connection = (new URL(url)).openConnection();
|
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
|
||||||
connection.addRequestProperty("user-agent", "Mozilla/5.0");
|
connection.setConnectTimeout(TURN_HTTP_TIMEOUT_MS);
|
||||||
connection.addRequestProperty("origin", "https://apprtc.appspot.com");
|
connection.setReadTimeout(TURN_HTTP_TIMEOUT_MS);
|
||||||
String response = drainStream(connection.getInputStream());
|
int responseCode = connection.getResponseCode();
|
||||||
|
if (responseCode != 200) {
|
||||||
|
throw new IOException("Non-200 response when requesting TURN server from "
|
||||||
|
+ url + " : " + connection.getHeaderField(null));
|
||||||
|
}
|
||||||
|
InputStream responseStream = connection.getInputStream();
|
||||||
|
String response = drainStream(responseStream);
|
||||||
|
connection.disconnect();
|
||||||
Log.d(TAG, "TURN response: " + response);
|
Log.d(TAG, "TURN response: " + response);
|
||||||
JSONObject responseJSON = new JSONObject(response);
|
JSONObject responseJSON = new JSONObject(response);
|
||||||
String username = responseJSON.getString("username");
|
String username = responseJSON.getString("username");
|
||||||
@ -317,7 +295,7 @@ public class RoomParametersFetcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Return the contents of an InputStream as a String.
|
// Return the contents of an InputStream as a String.
|
||||||
private String drainStream(InputStream in) {
|
private static String drainStream(InputStream in) {
|
||||||
Scanner s = new Scanner(in).useDelimiter("\\A");
|
Scanner s = new Scanner(in).useDelimiter("\\A");
|
||||||
return s.hasNext() ? s.next() : "";
|
return s.hasNext() ? s.next() : "";
|
||||||
}
|
}
|
||||||
|
@ -82,14 +82,12 @@ public class WebSocketChannelClient {
|
|||||||
* All events are dispatched from a looper executor thread.
|
* All events are dispatched from a looper executor thread.
|
||||||
*/
|
*/
|
||||||
public interface WebSocketChannelEvents {
|
public interface WebSocketChannelEvents {
|
||||||
public void onWebSocketOpen();
|
|
||||||
public void onWebSocketMessage(final String message);
|
public void onWebSocketMessage(final String message);
|
||||||
public void onWebSocketClose();
|
public void onWebSocketClose();
|
||||||
public void onWebSocketError(final String description);
|
public void onWebSocketError(final String description);
|
||||||
}
|
}
|
||||||
|
|
||||||
public WebSocketChannelClient(LooperExecutor executor,
|
public WebSocketChannelClient(LooperExecutor executor, WebSocketChannelEvents events) {
|
||||||
WebSocketChannelEvents events) {
|
|
||||||
this.executor = executor;
|
this.executor = executor;
|
||||||
this.events = events;
|
this.events = events;
|
||||||
roomID = null;
|
roomID = null;
|
||||||
@ -102,8 +100,7 @@ public class WebSocketChannelClient {
|
|||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void connect(final String wsUrl, final String postUrl,
|
public void connect(final String wsUrl, final String postUrl) {
|
||||||
final String roomID, final String clientID) {
|
|
||||||
checkIfCalledOnValidThread();
|
checkIfCalledOnValidThread();
|
||||||
if (state != WebSocketConnectionState.NEW) {
|
if (state != WebSocketConnectionState.NEW) {
|
||||||
Log.e(TAG, "WebSocket is already connected.");
|
Log.e(TAG, "WebSocket is already connected.");
|
||||||
@ -111,8 +108,6 @@ public class WebSocketChannelClient {
|
|||||||
}
|
}
|
||||||
wsServerUrl = wsUrl;
|
wsServerUrl = wsUrl;
|
||||||
postServerUrl = postUrl;
|
postServerUrl = postUrl;
|
||||||
this.roomID = roomID;
|
|
||||||
this.clientID = clientID;
|
|
||||||
closeEvent = false;
|
closeEvent = false;
|
||||||
|
|
||||||
Log.d(TAG, "Connecting WebSocket to: " + wsUrl + ". Post URL: " + postUrl);
|
Log.d(TAG, "Connecting WebSocket to: " + wsUrl + ". Post URL: " + postUrl);
|
||||||
@ -127,12 +122,15 @@ public class WebSocketChannelClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void register() {
|
public void register(final String roomID, final String clientID) {
|
||||||
checkIfCalledOnValidThread();
|
checkIfCalledOnValidThread();
|
||||||
|
this.roomID = roomID;
|
||||||
|
this.clientID = clientID;
|
||||||
if (state != WebSocketConnectionState.CONNECTED) {
|
if (state != WebSocketConnectionState.CONNECTED) {
|
||||||
Log.w(TAG, "WebSocket register() in state " + state);
|
Log.d(TAG, "WebSocket register() in state " + state);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
Log.d(TAG, "Registering WebSocket for room " + roomID + ". CLientID: " + clientID);
|
||||||
JSONObject json = new JSONObject();
|
JSONObject json = new JSONObject();
|
||||||
try {
|
try {
|
||||||
json.put("cmd", "register");
|
json.put("cmd", "register");
|
||||||
@ -271,7 +269,10 @@ public class WebSocketChannelClient {
|
|||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
state = WebSocketConnectionState.CONNECTED;
|
state = WebSocketConnectionState.CONNECTED;
|
||||||
events.onWebSocketOpen();
|
// Check if we have pending register request.
|
||||||
|
if (roomID != null && clientID != null) {
|
||||||
|
register(roomID, clientID);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -77,6 +77,7 @@ public class WebSocketRTCClient implements AppRTCClient,
|
|||||||
this.events = events;
|
this.events = events;
|
||||||
this.executor = executor;
|
this.executor = executor;
|
||||||
roomState = ConnectionState.NEW;
|
roomState = ConnectionState.NEW;
|
||||||
|
executor.requestStart();
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------------------------------------
|
// --------------------------------------------------------------------
|
||||||
@ -86,7 +87,6 @@ public class WebSocketRTCClient implements AppRTCClient,
|
|||||||
@Override
|
@Override
|
||||||
public void connectToRoom(RoomConnectionParameters connectionParameters) {
|
public void connectToRoom(RoomConnectionParameters connectionParameters) {
|
||||||
this.connectionParameters = connectionParameters;
|
this.connectionParameters = connectionParameters;
|
||||||
executor.requestStart();
|
|
||||||
executor.execute(new Runnable() {
|
executor.execute(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
@ -131,8 +131,7 @@ public class WebSocketRTCClient implements AppRTCClient,
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
new RoomParametersFetcher(connectionParameters.loopback, connectionUrl,
|
new RoomParametersFetcher(connectionUrl, null, callbacks).makeRequest();
|
||||||
null, callbacks).makeRequest();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disconnect from room and send bye messages - runs on a local looper thread.
|
// Disconnect from room and send bye messages - runs on a local looper thread.
|
||||||
@ -193,9 +192,9 @@ public class WebSocketRTCClient implements AppRTCClient,
|
|||||||
// Fire connection and signaling parameters events.
|
// Fire connection and signaling parameters events.
|
||||||
events.onConnectedToRoom(signalingParameters);
|
events.onConnectedToRoom(signalingParameters);
|
||||||
|
|
||||||
// Connect to WebSocket server.
|
// Connect and register WebSocket client.
|
||||||
wsClient.connect(signalingParameters.wssUrl, signalingParameters.wssPostUrl,
|
wsClient.connect(signalingParameters.wssUrl, signalingParameters.wssPostUrl);
|
||||||
connectionParameters.roomId, signalingParameters.clientId);
|
wsClient.register(connectionParameters.roomId, signalingParameters.clientId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send local offer SDP to the other participant.
|
// Send local offer SDP to the other participant.
|
||||||
@ -274,12 +273,6 @@ public class WebSocketRTCClient implements AppRTCClient,
|
|||||||
// WebSocketChannelEvents interface implementation.
|
// WebSocketChannelEvents interface implementation.
|
||||||
// All events are called by WebSocketChannelClient on a local looper thread
|
// All events are called by WebSocketChannelClient on a local looper thread
|
||||||
// (passed to WebSocket client constructor).
|
// (passed to WebSocket client constructor).
|
||||||
@Override
|
|
||||||
public void onWebSocketOpen() {
|
|
||||||
Log.d(TAG, "Websocket connection completed. Registering...");
|
|
||||||
wsClient.register();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onWebSocketMessage(final String msg) {
|
public void onWebSocketMessage(final String msg) {
|
||||||
if (wsClient.getState() != WebSocketConnectionState.REGISTERED) {
|
if (wsClient.getState() != WebSocketConnectionState.REGISTERED) {
|
||||||
|
@ -40,6 +40,7 @@ import java.util.Scanner;
|
|||||||
*/
|
*/
|
||||||
public class AsyncHttpURLConnection {
|
public class AsyncHttpURLConnection {
|
||||||
private static final int HTTP_TIMEOUT_MS = 8000;
|
private static final int HTTP_TIMEOUT_MS = 8000;
|
||||||
|
private static final String HTTP_ORIGIN = "https://apprtc.appspot.com";
|
||||||
private final String method;
|
private final String method;
|
||||||
private final String url;
|
private final String url;
|
||||||
private final String message;
|
private final String message;
|
||||||
@ -83,6 +84,8 @@ public class AsyncHttpURLConnection {
|
|||||||
connection.setDoInput(true);
|
connection.setDoInput(true);
|
||||||
connection.setConnectTimeout(HTTP_TIMEOUT_MS);
|
connection.setConnectTimeout(HTTP_TIMEOUT_MS);
|
||||||
connection.setReadTimeout(HTTP_TIMEOUT_MS);
|
connection.setReadTimeout(HTTP_TIMEOUT_MS);
|
||||||
|
// TODO(glaznev) - query request origin from pref_room_server_url_key preferences.
|
||||||
|
connection.addRequestProperty("origin", HTTP_ORIGIN);
|
||||||
boolean doOutput = false;
|
boolean doOutput = false;
|
||||||
if (method.equals("POST")) {
|
if (method.equals("POST")) {
|
||||||
doOutput = true;
|
doOutput = true;
|
||||||
@ -102,6 +105,7 @@ public class AsyncHttpURLConnection {
|
|||||||
// Get response.
|
// Get response.
|
||||||
int responseCode = connection.getResponseCode();
|
int responseCode = connection.getResponseCode();
|
||||||
if (responseCode != 200) {
|
if (responseCode != 200) {
|
||||||
|
connection.disconnect();
|
||||||
events.onHttpError("Non-200 response to " + method + " to URL: "
|
events.onHttpError("Non-200 response to " + method + " to URL: "
|
||||||
+ url + " : " + connection.getHeaderField(null));
|
+ url + " : " + connection.getHeaderField(null));
|
||||||
return;
|
return;
|
||||||
@ -109,6 +113,7 @@ public class AsyncHttpURLConnection {
|
|||||||
InputStream responseStream = connection.getInputStream();
|
InputStream responseStream = connection.getInputStream();
|
||||||
String response = drainStream(responseStream);
|
String response = drainStream(responseStream);
|
||||||
responseStream.close();
|
responseStream.close();
|
||||||
|
connection.disconnect();
|
||||||
events.onHttpComplete(response);
|
events.onHttpComplete(response);
|
||||||
} catch (SocketTimeoutException e) {
|
} catch (SocketTimeoutException e) {
|
||||||
events.onHttpError("HTTP " + method + " to " + url + " timeout");
|
events.onHttpError("HTTP " + method + " to " + url + " timeout");
|
||||||
|
@ -85,7 +85,7 @@ public class LooperExecutor extends Thread implements Executor {
|
|||||||
handler.post(new Runnable() {
|
handler.post(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
Looper.myLooper().quitSafely();
|
Looper.myLooper().quit();
|
||||||
Log.d(TAG, "Looper thread finished.");
|
Log.d(TAG, "Looper thread finished.");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user