Merge fixes and changed for Android AppRTCDemo from internal repo.
- Rename AppRTCDemoActivity to CallActivity and move UI controls to a fragment. - Add option to enable/disable statistics. - Move peer connection and video constraints from URL to peer connection client. - Variable renaming. R=jiayl@webrtc.org, wzh@webrtc.org Review URL: https://webrtc-codereview.appspot.com/33299004 Cr-Commit-Position: refs/heads/master@{#8319} git-svn-id: http://webrtc.googlecode.com/svn/trunk@8319 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
@ -39,11 +39,11 @@
|
|||||||
android:label="@string/settings_name">
|
android:label="@string/settings_name">
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity android:name="AppRTCDemoActivity"
|
<activity android:name="CallActivity"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:screenOrientation="fullUser"
|
android:screenOrientation="fullUser"
|
||||||
android:configChanges="orientation|screenSize"
|
android:configChanges="orientation|screenSize"
|
||||||
android:theme="@style/AppRTCDemoActivityTheme">
|
android:theme="@style/CallActivityTheme">
|
||||||
</activity>
|
</activity>
|
||||||
</application>
|
</application>
|
||||||
</manifest>
|
</manifest>
|
||||||
|
20
talk/examples/android/res/layout/activity_call.xml
Normal file
20
talk/examples/android/res/layout/activity_call.xml
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<?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"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<android.opengl.GLSurfaceView
|
||||||
|
android:id="@+id/glview_call"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent" />
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/call_fragment_container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent" />
|
||||||
|
|
||||||
|
</RelativeLayout>
|
@ -1,42 +0,0 @@
|
|||||||
<?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"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<android.opengl.GLSurfaceView
|
|
||||||
android:id="@+id/glview"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/encoder_stat"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_alignParentRight="true"
|
|
||||||
android:textStyle="bold"
|
|
||||||
android:textColor="#C000FF00"
|
|
||||||
android:textSize="12dp"
|
|
||||||
android:layout_margin="8dp"/>
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/room_name"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_centerHorizontal="true"
|
|
||||||
android:layout_above="@+id/menubar_fragment"
|
|
||||||
android:textSize="24sp"
|
|
||||||
android:layout_margin="8dp"/>
|
|
||||||
<fragment
|
|
||||||
android:name="org.appspot.apprtc.AppRTCDemoActivity$MenuBarFragment"
|
|
||||||
android:id="@+id/menubar_fragment"
|
|
||||||
android:layout_centerHorizontal="true"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_alignParentBottom="true"
|
|
||||||
android:layout_marginBottom="32dp"
|
|
||||||
tools:layout="@layout/fragment_menubar"/>
|
|
||||||
|
|
||||||
</RelativeLayout>
|
|
79
talk/examples/android/res/layout/fragment_call.xml
Normal file
79
talk/examples/android/res/layout/fragment_call.xml
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
<?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"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/encoder_stat_call"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentRight="true"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textColor="#C000FF00"
|
||||||
|
android:textSize="12dp"
|
||||||
|
android:layout_margin="8dp"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/contact_name_call"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_centerHorizontal="true"
|
||||||
|
android:layout_above="@+id/buttons_call_container"
|
||||||
|
android:textSize="24sp"
|
||||||
|
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_alignParentLeft="true"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"/>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/buttons_call_container"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_alignParentBottom="true"
|
||||||
|
android:layout_marginBottom="32dp"
|
||||||
|
android:layout_centerHorizontal="true"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/button_call_disconnect"
|
||||||
|
android:background="@drawable/disconnect"
|
||||||
|
android:contentDescription="@string/disconnect_call"
|
||||||
|
android:layout_marginRight="16dp"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"/>
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/button_call_switch_camera"
|
||||||
|
android:background="@android:drawable/ic_menu_camera"
|
||||||
|
android:contentDescription="@string/switch_camera"
|
||||||
|
android:layout_marginRight="8dp"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"/>
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/button_call_scaling_mode"
|
||||||
|
android:background="@drawable/ic_action_return_from_full_screen"
|
||||||
|
android:contentDescription="@string/disconnect_call"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</RelativeLayout>
|
@ -1,45 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
|
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
tools:context="org.appspot.apprtc.AppRTCDemoActivity$MenuBarFragment"
|
|
||||||
android:id="@+id/menubar"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_gravity="center_vertical|center_horizontal">
|
|
||||||
|
|
||||||
<ImageButton
|
|
||||||
android:id="@+id/button_disconnect"
|
|
||||||
android:background="@drawable/disconnect"
|
|
||||||
android:contentDescription="@string/disconnect_call"
|
|
||||||
android:layout_marginRight="16dp"
|
|
||||||
android:layout_width="48dp"
|
|
||||||
android:layout_height="48dp"/>
|
|
||||||
|
|
||||||
<!-- TODO(kjellander): Add audio and video mute buttons. -->
|
|
||||||
|
|
||||||
<ImageButton
|
|
||||||
android:id="@+id/button_switch_camera"
|
|
||||||
android:background="@android:drawable/ic_menu_camera"
|
|
||||||
android:contentDescription="@string/switch_camera"
|
|
||||||
android:layout_marginRight="8dp"
|
|
||||||
android:layout_width="48dp"
|
|
||||||
android:layout_height="48dp"/>
|
|
||||||
|
|
||||||
<ImageButton
|
|
||||||
android:id="@+id/button_toggle_debug"
|
|
||||||
android:background="@android:drawable/ic_menu_info_details"
|
|
||||||
android:contentDescription="@string/disconnect_call"
|
|
||||||
android:layout_marginRight="8dp"
|
|
||||||
android:layout_width="48dp"
|
|
||||||
android:layout_height="48dp"/>
|
|
||||||
|
|
||||||
<ImageButton
|
|
||||||
android:id="@+id/button_scaling_mode"
|
|
||||||
android:background="@drawable/ic_action_return_from_full_screen"
|
|
||||||
android:contentDescription="@string/disconnect_call"
|
|
||||||
android:layout_width="48dp"
|
|
||||||
android:layout_height="48dp"/>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
@ -17,6 +17,7 @@
|
|||||||
<string name="missing_url">FATAL ERROR: Missing URL to connect to.</string>
|
<string name="missing_url">FATAL ERROR: Missing URL to connect to.</string>
|
||||||
<string name="ok">OK</string>
|
<string name="ok">OK</string>
|
||||||
<string name="switch_camera">Switch front/back camera</string>
|
<string name="switch_camera">Switch front/back camera</string>
|
||||||
|
<string name="toggle_debug">Toggle debug view</string>
|
||||||
<string name="action_settings">Settings</string>
|
<string name="action_settings">Settings</string>
|
||||||
<string name="add_room_description">Add new room to the list</string>
|
<string name="add_room_description">Add new room to the list</string>
|
||||||
<string name="remove_room_description">Remove room from the list</string>
|
<string name="remove_room_description">Remove room from the list</string>
|
||||||
@ -40,7 +41,7 @@
|
|||||||
<string name="pref_cpu_usage_detection_key">cpu_usage_detection</string>
|
<string name="pref_cpu_usage_detection_key">cpu_usage_detection</string>
|
||||||
<string name="pref_cpu_usage_detection_title">CPU overuse detection.</string>
|
<string name="pref_cpu_usage_detection_title">CPU overuse detection.</string>
|
||||||
<string name="pref_cpu_usage_detection_dlg">Adapt transmission to CPU status.</string>
|
<string name="pref_cpu_usage_detection_dlg">Adapt transmission to CPU status.</string>
|
||||||
<string name="pref_cpu_usage_detection_default">true</string>
|
<string name="pref_cpu_usage_detection_default" translatable="false">true</string>
|
||||||
|
|
||||||
<string name="pref_startbitrate_key">startbitrate_preference</string>
|
<string name="pref_startbitrate_key">startbitrate_preference</string>
|
||||||
<string name="pref_startbitrate_title">Start bitrate setting.</string>
|
<string name="pref_startbitrate_title">Start bitrate setting.</string>
|
||||||
@ -68,6 +69,11 @@
|
|||||||
<string name="pref_room_server_url_key">room_server_url_preference</string>
|
<string name="pref_room_server_url_key">room_server_url_preference</string>
|
||||||
<string name="pref_room_server_url_title">Room server URL.</string>
|
<string name="pref_room_server_url_title">Room server URL.</string>
|
||||||
<string name="pref_room_server_url_dlg">Enter a room server URL.</string>
|
<string name="pref_room_server_url_dlg">Enter a room server URL.</string>
|
||||||
<string name="pref_room_server_url_default">https://apprtc.appspot.com</string>
|
<string name="pref_room_server_url_default" translatable="false">https://apprtc.appspot.com</string>
|
||||||
|
|
||||||
|
<string name="pref_displayhud_key">displayhud_preference</string>
|
||||||
|
<string name="pref_displayhud_title">Display call statistics.</string>
|
||||||
|
<string name="pref_displayhud_dlg">Display call statistics.</string>
|
||||||
|
<string name="pref_displayhud_default" translatable="false">false</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<style name="AppRTCDemoActivityTheme" parent="android:Theme.Black">
|
<style name="CallActivityTheme" parent="android:Theme.Black">
|
||||||
<item name="android:windowActionBar">false</item>
|
<item name="android:windowActionBar">false</item>
|
||||||
<item name="android:windowFullscreen">true</item>
|
<item name="android:windowFullscreen">true</item>
|
||||||
<item name="android:windowNoTitle">true</item>
|
<item name="android:windowNoTitle">true</item>
|
||||||
|
@ -58,4 +58,10 @@
|
|||||||
android:defaultValue="@string/pref_room_server_url_default"
|
android:defaultValue="@string/pref_room_server_url_default"
|
||||||
android:dialogTitle="@string/pref_room_server_url_dlg" />
|
android:dialogTitle="@string/pref_room_server_url_dlg" />
|
||||||
|
|
||||||
|
<CheckBoxPreference
|
||||||
|
android:key="@string/pref_displayhud_key"
|
||||||
|
android:title="@string/pref_displayhud_title"
|
||||||
|
android:dialogTitle="@string/pref_displayhud_dlg"
|
||||||
|
android:defaultValue="@string/pref_displayhud_default" />
|
||||||
|
|
||||||
</PreferenceScreen>
|
</PreferenceScreen>
|
||||||
|
@ -38,12 +38,28 @@ import java.util.List;
|
|||||||
* AppRTCClient is the interface representing an AppRTC client.
|
* AppRTCClient is the interface representing an AppRTC client.
|
||||||
*/
|
*/
|
||||||
public interface AppRTCClient {
|
public interface AppRTCClient {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Asynchronously connect to an AppRTC room URL, e.g.
|
* Struct holding the connection parameters of an AppRTC room.
|
||||||
* https://apprtc.appspot.com/?r=NNN. Once connection is established
|
|
||||||
* onConnectedToRoom() callback with room parameters is invoked.
|
|
||||||
*/
|
*/
|
||||||
public void connectToRoom(final String url, final boolean loopback);
|
public static class RoomConnectionParameters {
|
||||||
|
public final String roomUrl;
|
||||||
|
public final String roomId;
|
||||||
|
public final boolean loopback;
|
||||||
|
public RoomConnectionParameters(
|
||||||
|
String roomUrl, String roomId, boolean loopback) {
|
||||||
|
this.roomUrl = roomUrl;
|
||||||
|
this.roomId = roomId;
|
||||||
|
this.loopback = loopback;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asynchronously connect to an AppRTC room URL using supplied connection
|
||||||
|
* parameters. Once connection is established onConnectedToRoom()
|
||||||
|
* callback with room parameters is invoked.
|
||||||
|
*/
|
||||||
|
public void connectToRoom(RoomConnectionParameters connectionParameters);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send offer SDP to the other participant.
|
* Send offer SDP to the other participant.
|
||||||
@ -68,13 +84,12 @@ public interface AppRTCClient {
|
|||||||
/**
|
/**
|
||||||
* Struct holding the signaling parameters of an AppRTC room.
|
* Struct holding the signaling parameters of an AppRTC room.
|
||||||
*/
|
*/
|
||||||
public class SignalingParameters {
|
public static class SignalingParameters {
|
||||||
public final List<PeerConnection.IceServer> iceServers;
|
public final List<PeerConnection.IceServer> iceServers;
|
||||||
public final boolean initiator;
|
public final boolean initiator;
|
||||||
public final MediaConstraints pcConstraints;
|
public final MediaConstraints pcConstraints;
|
||||||
public final MediaConstraints videoConstraints;
|
public final MediaConstraints videoConstraints;
|
||||||
public final MediaConstraints audioConstraints;
|
public final MediaConstraints audioConstraints;
|
||||||
public final String roomId;
|
|
||||||
public final String clientId;
|
public final String clientId;
|
||||||
public final String wssUrl;
|
public final String wssUrl;
|
||||||
public final String wssPostUrl;
|
public final String wssPostUrl;
|
||||||
@ -85,15 +100,13 @@ public interface AppRTCClient {
|
|||||||
List<PeerConnection.IceServer> iceServers,
|
List<PeerConnection.IceServer> iceServers,
|
||||||
boolean initiator, MediaConstraints pcConstraints,
|
boolean initiator, MediaConstraints pcConstraints,
|
||||||
MediaConstraints videoConstraints, MediaConstraints audioConstraints,
|
MediaConstraints videoConstraints, MediaConstraints audioConstraints,
|
||||||
String roomId, String clientId,
|
String clientId, String wssUrl, String wssPostUrl,
|
||||||
String wssUrl, String wssPostUrl,
|
|
||||||
SessionDescription offerSdp, List<IceCandidate> iceCandidates) {
|
SessionDescription offerSdp, List<IceCandidate> iceCandidates) {
|
||||||
this.iceServers = iceServers;
|
this.iceServers = iceServers;
|
||||||
this.initiator = initiator;
|
this.initiator = initiator;
|
||||||
this.pcConstraints = pcConstraints;
|
this.pcConstraints = pcConstraints;
|
||||||
this.videoConstraints = videoConstraints;
|
this.videoConstraints = videoConstraints;
|
||||||
this.audioConstraints = audioConstraints;
|
this.audioConstraints = audioConstraints;
|
||||||
this.roomId = roomId;
|
|
||||||
this.clientId = clientId;
|
this.clientId = clientId;
|
||||||
this.wssUrl = wssUrl;
|
this.wssUrl = wssUrl;
|
||||||
this.wssPostUrl = wssPostUrl;
|
this.wssPostUrl = wssPostUrl;
|
||||||
|
@ -1,691 +0,0 @@
|
|||||||
/*
|
|
||||||
* libjingle
|
|
||||||
* Copyright 2013 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 org.appspot.apprtc.AppRTCClient.SignalingParameters;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.app.AlertDialog;
|
|
||||||
import android.app.Fragment;
|
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.graphics.Color;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.opengl.GLSurfaceView;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.util.TypedValue;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.view.ViewGroup.LayoutParams;
|
|
||||||
import android.view.Window;
|
|
||||||
import android.view.WindowManager;
|
|
||||||
import android.widget.ImageButton;
|
|
||||||
import android.widget.TextView;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import org.webrtc.IceCandidate;
|
|
||||||
import org.webrtc.SessionDescription;
|
|
||||||
import org.webrtc.StatsObserver;
|
|
||||||
import org.webrtc.StatsReport;
|
|
||||||
import org.webrtc.VideoRenderer;
|
|
||||||
import org.webrtc.VideoRendererGui;
|
|
||||||
import org.webrtc.VideoRendererGui.ScalingType;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Activity of the AppRTCDemo Android app demonstrating interoperability
|
|
||||||
* between the Android/Java implementation of PeerConnection and the
|
|
||||||
* apprtc.appspot.com demo webapp.
|
|
||||||
*/
|
|
||||||
public class AppRTCDemoActivity extends Activity
|
|
||||||
implements AppRTCClient.SignalingEvents,
|
|
||||||
PeerConnectionClient.PeerConnectionEvents {
|
|
||||||
private static final String TAG = "AppRTCClient";
|
|
||||||
private PeerConnectionClient pc = null;
|
|
||||||
private AppRTCClient appRtcClient;
|
|
||||||
private SignalingParameters signalingParameters;
|
|
||||||
private AppRTCAudioManager audioManager = null;
|
|
||||||
private View rootView;
|
|
||||||
private View menuBar;
|
|
||||||
private GLSurfaceView videoView;
|
|
||||||
private VideoRenderer.Callbacks localRender;
|
|
||||||
private VideoRenderer.Callbacks remoteRender;
|
|
||||||
private ScalingType scalingType;
|
|
||||||
private Toast logToast;
|
|
||||||
private final LayoutParams hudLayout =
|
|
||||||
new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
|
|
||||||
private TextView hudView;
|
|
||||||
private TextView encoderStatView;
|
|
||||||
private TextView roomNameView;
|
|
||||||
private ImageButton videoScalingButton;
|
|
||||||
private String roomName;
|
|
||||||
private boolean commandLineRun;
|
|
||||||
private boolean activityRunning;
|
|
||||||
private int runTimeMs;
|
|
||||||
private int startBitrate;
|
|
||||||
private String videoCodec;
|
|
||||||
private boolean hwCodecAcceleration;
|
|
||||||
private boolean iceConnected;
|
|
||||||
private boolean isError;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
|
|
||||||
// Set window styles for fullscreen-window size. Needs to be done before
|
|
||||||
// adding content.
|
|
||||||
requestWindowFeature(Window.FEATURE_NO_TITLE);
|
|
||||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
|
||||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
|
||||||
getWindow().getDecorView().setSystemUiVisibility(
|
|
||||||
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
|
||||||
| View.SYSTEM_UI_FLAG_FULLSCREEN
|
|
||||||
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
|
|
||||||
|
|
||||||
setContentView(R.layout.activity_fullscreen);
|
|
||||||
|
|
||||||
Thread.setDefaultUncaughtExceptionHandler(
|
|
||||||
new UnhandledExceptionHandler(this));
|
|
||||||
iceConnected = false;
|
|
||||||
signalingParameters = null;
|
|
||||||
|
|
||||||
rootView = findViewById(android.R.id.content);
|
|
||||||
encoderStatView = (TextView) findViewById(R.id.encoder_stat);
|
|
||||||
menuBar = findViewById(R.id.menubar_fragment);
|
|
||||||
roomNameView = (TextView) findViewById(R.id.room_name);
|
|
||||||
videoView = (GLSurfaceView) findViewById(R.id.glview);
|
|
||||||
|
|
||||||
VideoRendererGui.setView(videoView, new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
createPeerConnectionFactory();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
scalingType = ScalingType.SCALE_ASPECT_FILL;
|
|
||||||
remoteRender = VideoRendererGui.create(0, 0, 100, 100, scalingType, false);
|
|
||||||
localRender = VideoRendererGui.create(0, 0, 100, 100, scalingType, true);
|
|
||||||
|
|
||||||
videoView.setOnClickListener(
|
|
||||||
new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View view) {
|
|
||||||
int visibility = menuBar.getVisibility() == View.VISIBLE
|
|
||||||
? View.INVISIBLE : View.VISIBLE;
|
|
||||||
encoderStatView.setVisibility(visibility);
|
|
||||||
menuBar.setVisibility(visibility);
|
|
||||||
roomNameView.setVisibility(visibility);
|
|
||||||
if (visibility == View.VISIBLE) {
|
|
||||||
encoderStatView.bringToFront();
|
|
||||||
menuBar.bringToFront();
|
|
||||||
roomNameView.bringToFront();
|
|
||||||
rootView.invalidate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
((ImageButton) findViewById(R.id.button_disconnect)).setOnClickListener(
|
|
||||||
new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View view) {
|
|
||||||
logAndToast("Disconnecting call.");
|
|
||||||
disconnect();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
((ImageButton) findViewById(R.id.button_switch_camera)).setOnClickListener(
|
|
||||||
new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View view) {
|
|
||||||
if (pc != null) {
|
|
||||||
pc.switchCamera();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
((ImageButton) findViewById(R.id.button_toggle_debug)).setOnClickListener(
|
|
||||||
new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View view) {
|
|
||||||
int visibility = hudView.getVisibility() == View.VISIBLE
|
|
||||||
? View.INVISIBLE : View.VISIBLE;
|
|
||||||
hudView.setVisibility(visibility);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
videoScalingButton = (ImageButton) findViewById(R.id.button_scaling_mode);
|
|
||||||
videoScalingButton.setOnClickListener(
|
|
||||||
new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View view) {
|
|
||||||
if (scalingType == ScalingType.SCALE_ASPECT_FILL) {
|
|
||||||
videoScalingButton.setBackgroundResource(
|
|
||||||
R.drawable.ic_action_full_screen);
|
|
||||||
scalingType = ScalingType.SCALE_ASPECT_FIT;
|
|
||||||
} else {
|
|
||||||
videoScalingButton.setBackgroundResource(
|
|
||||||
R.drawable.ic_action_return_from_full_screen);
|
|
||||||
scalingType = ScalingType.SCALE_ASPECT_FILL;
|
|
||||||
}
|
|
||||||
updateVideoView();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
hudView = new TextView(this);
|
|
||||||
hudView.setTextColor(Color.BLACK);
|
|
||||||
hudView.setBackgroundColor(Color.WHITE);
|
|
||||||
hudView.setAlpha(0.4f);
|
|
||||||
hudView.setTextSize(TypedValue.COMPLEX_UNIT_PT, 5);
|
|
||||||
hudView.setVisibility(View.INVISIBLE);
|
|
||||||
addContentView(hudView, hudLayout);
|
|
||||||
|
|
||||||
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(
|
|
||||||
ConnectActivity.EXTRA_CMDLINE, false);
|
|
||||||
runTimeMs = intent.getIntExtra(ConnectActivity.EXTRA_RUNTIME, 0);
|
|
||||||
startBitrate = intent.getIntExtra(ConnectActivity.EXTRA_BITRATE, 0);
|
|
||||||
if (intent.hasExtra(ConnectActivity.EXTRA_VIDEOCODEC)) {
|
|
||||||
videoCodec = intent.getStringExtra(ConnectActivity.EXTRA_VIDEOCODEC);
|
|
||||||
} else {
|
|
||||||
videoCodec = PeerConnectionClient.VIDEO_CODEC_VP8; // use VP8 by default.
|
|
||||||
}
|
|
||||||
hwCodecAcceleration = intent.getBooleanExtra(
|
|
||||||
ConnectActivity.EXTRA_HWCODEC, true);
|
|
||||||
|
|
||||||
if (url != null) {
|
|
||||||
if (loopback || (roomName != null && !roomName.equals(""))) {
|
|
||||||
// Start room connection.
|
|
||||||
logAndToast(getString(R.string.connecting_to, url));
|
|
||||||
appRtcClient = new WebSocketRTCClient(this);
|
|
||||||
appRtcClient.connectToRoom(url.toString(), loopback);
|
|
||||||
if (loopback) {
|
|
||||||
roomNameView.setText("loopback");
|
|
||||||
} else {
|
|
||||||
roomNameView.setText(roomName);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create and audio manager that will take care of audio routing,
|
|
||||||
// audio modes, audio device enumeration etc.
|
|
||||||
audioManager = AppRTCAudioManager.create(this, new Runnable() {
|
|
||||||
// This method will be called each time the audio state (number and
|
|
||||||
// type of devices) has been changed.
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
onAudioManagerChangedState();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
// Store existing audio settings and change audio mode to
|
|
||||||
// MODE_IN_COMMUNICATION for best possible VoIP performance.
|
|
||||||
Log.d(TAG, "Initializing the audio manager...");
|
|
||||||
audioManager.init();
|
|
||||||
|
|
||||||
// For command line execution run connection for <runTimeMs> and exit.
|
|
||||||
if (commandLineRun && runTimeMs > 0) {
|
|
||||||
videoView.postDelayed(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
disconnect();
|
|
||||||
}
|
|
||||||
}, runTimeMs);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
logAndToast("Empty or missing room name!");
|
|
||||||
setResult(RESULT_CANCELED);
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
logAndToast(getString(R.string.missing_url));
|
|
||||||
Log.e(TAG, "Didn't get any URL in intent!");
|
|
||||||
setResult(RESULT_CANCELED);
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create peer connection factory when EGL context is ready.
|
|
||||||
private void createPeerConnectionFactory() {
|
|
||||||
runOnUiThread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
if (pc == null) {
|
|
||||||
pc = new PeerConnectionClient();
|
|
||||||
pc.createPeerConnectionFactory(AppRTCDemoActivity.this,
|
|
||||||
videoCodec, hwCodecAcceleration,
|
|
||||||
VideoRendererGui.getEGLContext(), AppRTCDemoActivity.this);
|
|
||||||
}
|
|
||||||
if (signalingParameters != null) {
|
|
||||||
Log.w(TAG, "EGL context is ready after room connection.");
|
|
||||||
onConnectedToRoomInternal(signalingParameters);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* MenuBar fragment for AppRTC.
|
|
||||||
*/
|
|
||||||
public static class MenuBarFragment extends Fragment {
|
|
||||||
@Override
|
|
||||||
public View onCreateView(
|
|
||||||
LayoutInflater inflater,
|
|
||||||
ViewGroup container,
|
|
||||||
Bundle savedInstanceState) {
|
|
||||||
return inflater.inflate(R.layout.fragment_menubar, container, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPause() {
|
|
||||||
super.onPause();
|
|
||||||
videoView.onPause();
|
|
||||||
activityRunning = false;
|
|
||||||
if (pc != null) {
|
|
||||||
pc.stopVideoSource();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onResume() {
|
|
||||||
super.onResume();
|
|
||||||
videoView.onResume();
|
|
||||||
activityRunning = true;
|
|
||||||
if (pc != null) {
|
|
||||||
pc.startVideoSource();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onDestroy() {
|
|
||||||
disconnect();
|
|
||||||
super.onDestroy();
|
|
||||||
if (logToast != null) {
|
|
||||||
logToast.cancel();
|
|
||||||
}
|
|
||||||
activityRunning = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateVideoView() {
|
|
||||||
VideoRendererGui.update(remoteRender, 0, 0, 100, 100, scalingType);
|
|
||||||
if (iceConnected) {
|
|
||||||
VideoRendererGui.update(localRender, 70, 70, 28, 28,
|
|
||||||
ScalingType.SCALE_ASPECT_FIT);
|
|
||||||
} else {
|
|
||||||
VideoRendererGui.update(localRender, 0, 0, 100, 100, scalingType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onAudioManagerChangedState() {
|
|
||||||
// TODO(henrika): disable video if AppRTCAudioManager.AudioDevice.EARPIECE
|
|
||||||
// is active.
|
|
||||||
}
|
|
||||||
|
|
||||||
// Disconnect from remote resources, dispose of local resources, and exit.
|
|
||||||
private void disconnect() {
|
|
||||||
if (appRtcClient != null) {
|
|
||||||
appRtcClient.disconnectFromRoom();
|
|
||||||
appRtcClient = null;
|
|
||||||
}
|
|
||||||
if (pc != null) {
|
|
||||||
pc.close();
|
|
||||||
pc = null;
|
|
||||||
}
|
|
||||||
if (audioManager != null) {
|
|
||||||
audioManager.close();
|
|
||||||
audioManager = null;
|
|
||||||
}
|
|
||||||
if (iceConnected && !isError) {
|
|
||||||
setResult(RESULT_OK);
|
|
||||||
} else {
|
|
||||||
setResult(RESULT_CANCELED);
|
|
||||||
}
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void disconnectWithErrorMessage(final String errorMessage) {
|
|
||||||
if (commandLineRun || !activityRunning) {
|
|
||||||
Log.e(TAG, "Critical error: " + errorMessage);
|
|
||||||
disconnect();
|
|
||||||
} else {
|
|
||||||
new AlertDialog.Builder(this)
|
|
||||||
.setTitle(getText(R.string.channel_error_title))
|
|
||||||
.setMessage(errorMessage)
|
|
||||||
.setCancelable(false)
|
|
||||||
.setNeutralButton(R.string.ok, new DialogInterface.OnClickListener() {
|
|
||||||
public void onClick(DialogInterface dialog, int id) {
|
|
||||||
dialog.cancel();
|
|
||||||
disconnect();
|
|
||||||
}
|
|
||||||
}).create().show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log |msg| and Toast about it.
|
|
||||||
private void logAndToast(String msg) {
|
|
||||||
Log.d(TAG, msg);
|
|
||||||
if (logToast != null) {
|
|
||||||
logToast.cancel();
|
|
||||||
}
|
|
||||||
logToast = Toast.makeText(this, msg, Toast.LENGTH_SHORT);
|
|
||||||
logToast.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the active connection stats,
|
|
||||||
// or null if active connection is not found.
|
|
||||||
private String getActiveConnectionStats(StatsReport report) {
|
|
||||||
StringBuilder activeConnectionbuilder = new StringBuilder();
|
|
||||||
// googCandidatePair to show information about the active
|
|
||||||
// connection.
|
|
||||||
for (StatsReport.Value value : report.values) {
|
|
||||||
if (value.name.equals("googActiveConnection")
|
|
||||||
&& value.value.equals("false")) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
String name = value.name.replace("goog", "");
|
|
||||||
activeConnectionbuilder.append(name).append("=")
|
|
||||||
.append(value.value).append("\n");
|
|
||||||
}
|
|
||||||
return activeConnectionbuilder.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the heads-up display with information from |reports|.
|
|
||||||
private void updateHUD(StatsReport[] reports) {
|
|
||||||
StringBuilder builder = new StringBuilder();
|
|
||||||
for (StatsReport report : reports) {
|
|
||||||
Log.d(TAG, "Stats: " + report.toString());
|
|
||||||
// bweforvideo to show statistics for video Bandwidth Estimation,
|
|
||||||
// which is global per-session.
|
|
||||||
if (report.id.equals("bweforvideo")) {
|
|
||||||
for (StatsReport.Value value : report.values) {
|
|
||||||
String name = value.name.replace("goog", "")
|
|
||||||
.replace("Available", "").replace("Bandwidth", "")
|
|
||||||
.replace("Bitrate", "").replace("Enc", "");
|
|
||||||
|
|
||||||
builder.append(name).append("=").append(value.value)
|
|
||||||
.append(" ");
|
|
||||||
}
|
|
||||||
builder.append("\n");
|
|
||||||
} else if (report.type.equals("googCandidatePair")) {
|
|
||||||
String activeConnectionStats = getActiveConnectionStats(report);
|
|
||||||
if (activeConnectionStats == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
builder.append(activeConnectionStats);
|
|
||||||
} else {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
builder.append("\n");
|
|
||||||
}
|
|
||||||
hudView.setText(builder.toString() + hudView.getText());
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update encoder statistics view with information from |reports|.
|
|
||||||
private void updateEncoderStatistics(StatsReport[] reports) {
|
|
||||||
if (!iceConnected) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
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")) {
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
String stat = "";
|
|
||||||
if (fps != null) {
|
|
||||||
stat += "Fps: " + fps + "\n";
|
|
||||||
}
|
|
||||||
if (targetBitrate != null) {
|
|
||||||
stat += "Target BR: " + targetBitrate + "\n";
|
|
||||||
}
|
|
||||||
if (actualBitrate != null) {
|
|
||||||
stat += "Actual BR: " + actualBitrate;
|
|
||||||
}
|
|
||||||
encoderStatView.setText(stat);
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----Implementation of AppRTCClient.AppRTCSignalingEvents ---------------
|
|
||||||
// All callbacks are invoked from websocket signaling looper thread and
|
|
||||||
// are routed to UI thread.
|
|
||||||
private void onConnectedToRoomInternal(final SignalingParameters params) {
|
|
||||||
signalingParameters = params;
|
|
||||||
if (pc == null) {
|
|
||||||
Log.w(TAG, "Room is connected, but EGL context is not ready yet.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
logAndToast("Creating peer connection...");
|
|
||||||
pc.createPeerConnection(
|
|
||||||
localRender, remoteRender, signalingParameters, startBitrate);
|
|
||||||
|
|
||||||
// Schedule statistics display.
|
|
||||||
final Runnable repeatedStatsLogger = new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
if (pc == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final Runnable runnableThis = this;
|
|
||||||
if (hudView.getVisibility() == View.INVISIBLE
|
|
||||||
&& encoderStatView.getVisibility() == View.INVISIBLE) {
|
|
||||||
videoView.postDelayed(runnableThis, 1000);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
boolean success = pc.getStats(new StatsObserver() {
|
|
||||||
public void onComplete(final StatsReport[] reports) {
|
|
||||||
runOnUiThread(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
if (hudView.getVisibility() == View.VISIBLE) {
|
|
||||||
updateHUD(reports);
|
|
||||||
}
|
|
||||||
if (encoderStatView.getVisibility() == View.VISIBLE) {
|
|
||||||
updateEncoderStatistics(reports);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
videoView.postDelayed(runnableThis, 1000);
|
|
||||||
}
|
|
||||||
}, null);
|
|
||||||
if (!success) {
|
|
||||||
Log.w(TAG, "getStats() return false!");
|
|
||||||
videoView.postDelayed(runnableThis, 1000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
videoView.postDelayed(repeatedStatsLogger, 1000);
|
|
||||||
|
|
||||||
if (signalingParameters.initiator) {
|
|
||||||
logAndToast("Creating OFFER...");
|
|
||||||
// Create offer. Offer SDP will be sent to answering client in
|
|
||||||
// PeerConnectionEvents.onLocalDescription event.
|
|
||||||
pc.createOffer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onConnectedToRoom(final SignalingParameters params) {
|
|
||||||
runOnUiThread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
onConnectedToRoomInternal(params);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onRemoteDescription(final SessionDescription sdp) {
|
|
||||||
runOnUiThread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
if (pc == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
logAndToast("Received remote " + sdp.type + " ...");
|
|
||||||
pc.setRemoteDescription(sdp);
|
|
||||||
if (!signalingParameters.initiator) {
|
|
||||||
logAndToast("Creating ANSWER...");
|
|
||||||
// Create answer. Answer SDP will be sent to offering client in
|
|
||||||
// PeerConnectionEvents.onLocalDescription event.
|
|
||||||
pc.createAnswer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onRemoteIceCandidate(final IceCandidate candidate) {
|
|
||||||
runOnUiThread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
if (pc != null) {
|
|
||||||
pc.addRemoteIceCandidate(candidate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onChannelClose() {
|
|
||||||
runOnUiThread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
logAndToast("Remote end hung up; dropping PeerConnection");
|
|
||||||
disconnect();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onChannelError(final String description) {
|
|
||||||
runOnUiThread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
if (!isError) {
|
|
||||||
isError = true;
|
|
||||||
disconnectWithErrorMessage(description);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----Implementation of PeerConnectionClient.PeerConnectionEvents.---------
|
|
||||||
// Send local peer connection SDP and ICE candidates to remote party.
|
|
||||||
// All callbacks are invoked from peer connection client looper thread and
|
|
||||||
// are routed to UI thread.
|
|
||||||
@Override
|
|
||||||
public void onLocalDescription(final SessionDescription sdp) {
|
|
||||||
runOnUiThread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
if (appRtcClient != null) {
|
|
||||||
logAndToast("Sending " + sdp.type + " ...");
|
|
||||||
if (signalingParameters.initiator) {
|
|
||||||
appRtcClient.sendOfferSdp(sdp);
|
|
||||||
} else {
|
|
||||||
appRtcClient.sendAnswerSdp(sdp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onIceCandidate(final IceCandidate candidate) {
|
|
||||||
runOnUiThread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
if (appRtcClient != null) {
|
|
||||||
appRtcClient.sendLocalIceCandidate(candidate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onIceConnected() {
|
|
||||||
runOnUiThread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
logAndToast("ICE connected");
|
|
||||||
iceConnected = true;
|
|
||||||
updateVideoView();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onIceDisconnected() {
|
|
||||||
runOnUiThread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
logAndToast("ICE disconnected");
|
|
||||||
iceConnected = false;
|
|
||||||
disconnect();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPeerConnectionClosed() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPeerConnectionError(final String description) {
|
|
||||||
runOnUiThread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
if (!isError) {
|
|
||||||
isError = true;
|
|
||||||
disconnectWithErrorMessage(description);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
618
talk/examples/android/src/org/appspot/apprtc/CallActivity.java
Normal file
618
talk/examples/android/src/org/appspot/apprtc/CallActivity.java
Normal file
@ -0,0 +1,618 @@
|
|||||||
|
/*
|
||||||
|
* 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 org.appspot.apprtc.AppRTCClient.RoomConnectionParameters;
|
||||||
|
import org.appspot.apprtc.AppRTCClient.SignalingParameters;
|
||||||
|
import org.appspot.apprtc.PeerConnectionClient.PeerConnectionParameters;
|
||||||
|
import org.appspot.apprtc.util.LooperExecutor;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.app.AlertDialog;
|
||||||
|
import android.app.FragmentTransaction;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.opengl.GLSurfaceView;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.Window;
|
||||||
|
import android.view.WindowManager;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import org.webrtc.IceCandidate;
|
||||||
|
import org.webrtc.SessionDescription;
|
||||||
|
import org.webrtc.StatsReport;
|
||||||
|
import org.webrtc.VideoRenderer;
|
||||||
|
import org.webrtc.VideoRendererGui;
|
||||||
|
import org.webrtc.VideoRendererGui.ScalingType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Activity for peer connection call setup, call waiting
|
||||||
|
* and call view.
|
||||||
|
*/
|
||||||
|
public class CallActivity extends Activity
|
||||||
|
implements AppRTCClient.SignalingEvents,
|
||||||
|
PeerConnectionClient.PeerConnectionEvents,
|
||||||
|
CallFragment.OnCallEvents {
|
||||||
|
|
||||||
|
public static final String EXTRA_ROOMID =
|
||||||
|
"org.appspot.apprtc.ROOMID";
|
||||||
|
public static final String EXTRA_LOOPBACK =
|
||||||
|
"org.appspot.apprtc.LOOPBACK";
|
||||||
|
public static final String EXTRA_HWCODEC =
|
||||||
|
"org.appspot.apprtc.HWCODEC";
|
||||||
|
public static final String EXTRA_VIDEO_BITRATE =
|
||||||
|
"org.appspot.apprtc.VIDEO_BITRATE";
|
||||||
|
public static final String EXTRA_VIDEO_WIDTH =
|
||||||
|
"org.appspot.apprtc.VIDEO_WIDTH";
|
||||||
|
public static final String EXTRA_VIDEO_HEIGHT =
|
||||||
|
"org.appspot.apprtc.VIDEO_HEIGHT";
|
||||||
|
public static final String EXTRA_VIDEO_FPS =
|
||||||
|
"org.appspot.apprtc.VIDEO_FPS";
|
||||||
|
public static final String EXTRA_VIDEOCODEC =
|
||||||
|
"org.appspot.apprtc.VIDEOCODEC";
|
||||||
|
public static final String EXTRA_CPUOVERUSE_DETECTION =
|
||||||
|
"org.appspot.apprtc.CPUOVERUSE_DETECTION";
|
||||||
|
public static final String EXTRA_DISPLAY_HUD =
|
||||||
|
"org.appspot.apprtc.DISPLAY_HUD";
|
||||||
|
public static final String EXTRA_CMDLINE =
|
||||||
|
"org.appspot.apprtc.CMDLINE";
|
||||||
|
public static final String EXTRA_RUNTIME =
|
||||||
|
"org.appspot.apprtc.RUNTIME";
|
||||||
|
private static final String TAG = "CallRTCClient";
|
||||||
|
// Peer connection statistics callback period in ms.
|
||||||
|
private static final int STAT_CALLBACK_PERIOD = 1000;
|
||||||
|
// Local preview screen position before call is connected.
|
||||||
|
private static final int LOCAL_X_CONNECTING = 0;
|
||||||
|
private static final int LOCAL_Y_CONNECTING = 0;
|
||||||
|
private static final int LOCAL_WIDTH_CONNECTING = 100;
|
||||||
|
private static final int LOCAL_HEIGHT_CONNECTING = 100;
|
||||||
|
// Local preview screen position after call is connected.
|
||||||
|
private static final int LOCAL_X_CONNECTED = 72;
|
||||||
|
private static final int LOCAL_Y_CONNECTED = 72;
|
||||||
|
private static final int LOCAL_WIDTH_CONNECTED = 25;
|
||||||
|
private static final int LOCAL_HEIGHT_CONNECTED = 25;
|
||||||
|
// Remote video screen position
|
||||||
|
private static final int REMOTE_X = 0;
|
||||||
|
private static final int REMOTE_Y = 0;
|
||||||
|
private static final int REMOTE_WIDTH = 100;
|
||||||
|
private static final int REMOTE_HEIGHT = 100;
|
||||||
|
|
||||||
|
private PeerConnectionClient peerConnectionClient = null;
|
||||||
|
private AppRTCClient appRtcClient;
|
||||||
|
private SignalingParameters signalingParameters;
|
||||||
|
private AppRTCAudioManager audioManager = null;
|
||||||
|
private VideoRenderer.Callbacks localRender;
|
||||||
|
private VideoRenderer.Callbacks remoteRender;
|
||||||
|
private ScalingType scalingType;
|
||||||
|
private Toast logToast;
|
||||||
|
private boolean commandLineRun;
|
||||||
|
private int runTimeMs;
|
||||||
|
private boolean activityRunning;
|
||||||
|
private RoomConnectionParameters roomConnectionParameters;
|
||||||
|
private PeerConnectionParameters peerConnectionParameters;
|
||||||
|
private boolean hwCodecAcceleration;
|
||||||
|
private String videoCodec;
|
||||||
|
private boolean iceConnected;
|
||||||
|
private boolean isError;
|
||||||
|
private boolean callControlFragmentVisible = true;
|
||||||
|
|
||||||
|
// Controls
|
||||||
|
private GLSurfaceView videoView;
|
||||||
|
CallFragment callFragment;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
Thread.setDefaultUncaughtExceptionHandler(
|
||||||
|
new UnhandledExceptionHandler(this));
|
||||||
|
|
||||||
|
// Set window styles for fullscreen-window size. Needs to be done before
|
||||||
|
// adding content.
|
||||||
|
requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||||
|
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||||
|
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||||
|
getWindow().getDecorView().setSystemUiVisibility(
|
||||||
|
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
||||||
|
| View.SYSTEM_UI_FLAG_FULLSCREEN
|
||||||
|
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
|
||||||
|
setContentView(R.layout.activity_call);
|
||||||
|
|
||||||
|
iceConnected = false;
|
||||||
|
signalingParameters = null;
|
||||||
|
scalingType = ScalingType.SCALE_ASPECT_FILL;
|
||||||
|
|
||||||
|
// Create UI controls.
|
||||||
|
videoView = (GLSurfaceView) findViewById(R.id.glview_call);
|
||||||
|
callFragment = new CallFragment();
|
||||||
|
|
||||||
|
// Create video renderers.
|
||||||
|
VideoRendererGui.setView(videoView, new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
createPeerConnectionFactory();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
remoteRender = VideoRendererGui.create(
|
||||||
|
REMOTE_X, REMOTE_Y,
|
||||||
|
REMOTE_WIDTH, REMOTE_HEIGHT, scalingType, false);
|
||||||
|
localRender = VideoRendererGui.create(
|
||||||
|
LOCAL_X_CONNECTING, LOCAL_Y_CONNECTING,
|
||||||
|
LOCAL_WIDTH_CONNECTING, LOCAL_HEIGHT_CONNECTING, scalingType, true);
|
||||||
|
|
||||||
|
// Show/hide call control fragment on view click.
|
||||||
|
videoView.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
toggleCallControlFragmentVisibility();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Get Intent parameters.
|
||||||
|
final Intent intent = getIntent();
|
||||||
|
Uri roomUri = intent.getData();
|
||||||
|
if (roomUri == null) {
|
||||||
|
logAndToast(getString(R.string.missing_url));
|
||||||
|
Log.e(TAG, "Didn't get any URL in intent!");
|
||||||
|
setResult(RESULT_CANCELED);
|
||||||
|
finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String roomId = intent.getStringExtra(EXTRA_ROOMID);
|
||||||
|
if (roomId == null || roomId.length() == 0) {
|
||||||
|
logAndToast(getString(R.string.missing_url));
|
||||||
|
Log.e(TAG, "Incorrect room ID in intent!");
|
||||||
|
setResult(RESULT_CANCELED);
|
||||||
|
finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
boolean loopback = intent.getBooleanExtra(EXTRA_LOOPBACK, false);
|
||||||
|
hwCodecAcceleration = intent.getBooleanExtra(EXTRA_HWCODEC, true);
|
||||||
|
if (intent.hasExtra(EXTRA_VIDEOCODEC)) {
|
||||||
|
videoCodec = intent.getStringExtra(EXTRA_VIDEOCODEC);
|
||||||
|
} else {
|
||||||
|
videoCodec = PeerConnectionClient.VIDEO_CODEC_VP8; // use VP8 by default.
|
||||||
|
}
|
||||||
|
peerConnectionParameters = new PeerConnectionParameters(
|
||||||
|
intent.getIntExtra(EXTRA_VIDEO_WIDTH, 0),
|
||||||
|
intent.getIntExtra(EXTRA_VIDEO_HEIGHT, 0),
|
||||||
|
intent.getIntExtra(EXTRA_VIDEO_FPS, 0),
|
||||||
|
intent.getIntExtra(EXTRA_VIDEO_BITRATE, 0),
|
||||||
|
intent.getBooleanExtra(EXTRA_CPUOVERUSE_DETECTION, true));
|
||||||
|
commandLineRun = intent.getBooleanExtra(EXTRA_CMDLINE, false);
|
||||||
|
runTimeMs = intent.getIntExtra(EXTRA_RUNTIME, 0);
|
||||||
|
|
||||||
|
// Create connection client and connection parameters.
|
||||||
|
appRtcClient = new WebSocketRTCClient(this, new LooperExecutor());
|
||||||
|
roomConnectionParameters = new RoomConnectionParameters(
|
||||||
|
roomUri.toString(), roomId, loopback);
|
||||||
|
|
||||||
|
// Send intent arguments to fragment.
|
||||||
|
callFragment.setArguments(intent.getExtras());
|
||||||
|
// Activate call fragment and start the call.
|
||||||
|
getFragmentManager().beginTransaction()
|
||||||
|
.add(R.id.call_fragment_container, callFragment).commit();
|
||||||
|
startCall();
|
||||||
|
|
||||||
|
// For command line execution run connection for <runTimeMs> and exit.
|
||||||
|
if (commandLineRun && runTimeMs > 0) {
|
||||||
|
videoView.postDelayed(new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
disconnect();
|
||||||
|
}
|
||||||
|
}, runTimeMs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Activity interfaces
|
||||||
|
@Override
|
||||||
|
public void onPause() {
|
||||||
|
super.onPause();
|
||||||
|
videoView.onPause();
|
||||||
|
activityRunning = false;
|
||||||
|
if (peerConnectionClient != null) {
|
||||||
|
peerConnectionClient.stopVideoSource();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
videoView.onResume();
|
||||||
|
activityRunning = true;
|
||||||
|
if (peerConnectionClient != null) {
|
||||||
|
peerConnectionClient.startVideoSource();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDestroy() {
|
||||||
|
disconnect();
|
||||||
|
super.onDestroy();
|
||||||
|
if (logToast != null) {
|
||||||
|
logToast.cancel();
|
||||||
|
}
|
||||||
|
activityRunning = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CallFragment.OnCallEvents interface implementation.
|
||||||
|
@Override
|
||||||
|
public void onCallHangUp() {
|
||||||
|
disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCameraSwitch() {
|
||||||
|
if (peerConnectionClient != null) {
|
||||||
|
peerConnectionClient.switchCamera();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onVideoScalingSwitch(ScalingType scalingType) {
|
||||||
|
this.scalingType = scalingType;
|
||||||
|
updateVideoView();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper functions.
|
||||||
|
private void toggleCallControlFragmentVisibility() {
|
||||||
|
if (!iceConnected || !callFragment.isAdded()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Show/hide call control fragment
|
||||||
|
callControlFragmentVisible = !callControlFragmentVisible;
|
||||||
|
FragmentTransaction ft = getFragmentManager().beginTransaction();
|
||||||
|
if (callControlFragmentVisible) {
|
||||||
|
ft.show(callFragment);
|
||||||
|
} else {
|
||||||
|
ft.hide(callFragment);
|
||||||
|
}
|
||||||
|
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
|
||||||
|
ft.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateVideoView() {
|
||||||
|
VideoRendererGui.update(remoteRender,
|
||||||
|
REMOTE_X, REMOTE_Y,
|
||||||
|
REMOTE_WIDTH, REMOTE_HEIGHT, scalingType);
|
||||||
|
if (iceConnected) {
|
||||||
|
VideoRendererGui.update(localRender,
|
||||||
|
LOCAL_X_CONNECTED, LOCAL_Y_CONNECTED,
|
||||||
|
LOCAL_WIDTH_CONNECTED, LOCAL_HEIGHT_CONNECTED,
|
||||||
|
ScalingType.SCALE_ASPECT_FIT);
|
||||||
|
} else {
|
||||||
|
VideoRendererGui.update(localRender,
|
||||||
|
LOCAL_X_CONNECTING, LOCAL_Y_CONNECTING,
|
||||||
|
LOCAL_WIDTH_CONNECTING, LOCAL_HEIGHT_CONNECTING, scalingType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startCall() {
|
||||||
|
if (appRtcClient == null) {
|
||||||
|
Log.e(TAG, "AppRTC client is not allocated for a call.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Start room connection.
|
||||||
|
logAndToast(getString(R.string.connecting_to,
|
||||||
|
roomConnectionParameters.roomUrl));
|
||||||
|
appRtcClient.connectToRoom(roomConnectionParameters);
|
||||||
|
|
||||||
|
// Create and audio manager that will take care of audio routing,
|
||||||
|
// audio modes, audio device enumeration etc.
|
||||||
|
audioManager = AppRTCAudioManager.create(this, new Runnable() {
|
||||||
|
// This method will be called each time the audio state (number and
|
||||||
|
// type of devices) has been changed.
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
onAudioManagerChangedState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
// Store existing audio settings and change audio mode to
|
||||||
|
// MODE_IN_COMMUNICATION for best possible VoIP performance.
|
||||||
|
Log.d(TAG, "Initializing the audio manager...");
|
||||||
|
audioManager.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should be called from UI thread
|
||||||
|
private void callConnected() {
|
||||||
|
// Update video view.
|
||||||
|
updateVideoView();
|
||||||
|
// Enable statistics callback.
|
||||||
|
peerConnectionClient.enableStatsEvents(true, STAT_CALLBACK_PERIOD);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onAudioManagerChangedState() {
|
||||||
|
// TODO(henrika): disable video if AppRTCAudioManager.AudioDevice.EARPIECE
|
||||||
|
// is active.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create peer connection factory when EGL context is ready.
|
||||||
|
private void createPeerConnectionFactory() {
|
||||||
|
runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (peerConnectionClient == null) {
|
||||||
|
peerConnectionClient = new PeerConnectionClient();
|
||||||
|
peerConnectionClient.createPeerConnectionFactory(CallActivity.this,
|
||||||
|
videoCodec, hwCodecAcceleration,
|
||||||
|
VideoRendererGui.getEGLContext(), CallActivity.this);
|
||||||
|
}
|
||||||
|
if (signalingParameters != null) {
|
||||||
|
Log.w(TAG, "EGL context is ready after room connection.");
|
||||||
|
onConnectedToRoomInternal(signalingParameters);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disconnect from remote resources, dispose of local resources, and exit.
|
||||||
|
private void disconnect() {
|
||||||
|
if (appRtcClient != null) {
|
||||||
|
appRtcClient.disconnectFromRoom();
|
||||||
|
appRtcClient = null;
|
||||||
|
}
|
||||||
|
if (peerConnectionClient != null) {
|
||||||
|
peerConnectionClient.close();
|
||||||
|
peerConnectionClient = null;
|
||||||
|
}
|
||||||
|
if (audioManager != null) {
|
||||||
|
audioManager.close();
|
||||||
|
audioManager = null;
|
||||||
|
}
|
||||||
|
if (iceConnected && !isError) {
|
||||||
|
setResult(RESULT_OK);
|
||||||
|
} else {
|
||||||
|
setResult(RESULT_CANCELED);
|
||||||
|
}
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void disconnectWithErrorMessage(final String errorMessage) {
|
||||||
|
if (commandLineRun || !activityRunning) {
|
||||||
|
Log.e(TAG, "Critical error: " + errorMessage);
|
||||||
|
disconnect();
|
||||||
|
} else {
|
||||||
|
new AlertDialog.Builder(this)
|
||||||
|
.setTitle(getText(R.string.channel_error_title))
|
||||||
|
.setMessage(errorMessage)
|
||||||
|
.setCancelable(false)
|
||||||
|
.setNeutralButton(R.string.ok, new DialogInterface.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialog, int id) {
|
||||||
|
dialog.cancel();
|
||||||
|
disconnect();
|
||||||
|
}
|
||||||
|
}).create().show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log |msg| and Toast about it.
|
||||||
|
private void logAndToast(String msg) {
|
||||||
|
Log.d(TAG, msg);
|
||||||
|
if (logToast != null) {
|
||||||
|
logToast.cancel();
|
||||||
|
}
|
||||||
|
logToast = Toast.makeText(this, msg, Toast.LENGTH_SHORT);
|
||||||
|
logToast.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----Implementation of AppRTCClient.AppRTCSignalingEvents ---------------
|
||||||
|
// All callbacks are invoked from websocket signaling looper thread and
|
||||||
|
// are routed to UI thread.
|
||||||
|
private void onConnectedToRoomInternal(final SignalingParameters params) {
|
||||||
|
signalingParameters = params;
|
||||||
|
if (peerConnectionClient == null) {
|
||||||
|
Log.w(TAG, "Room is connected, but EGL context is not ready yet.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
logAndToast("Creating peer connection...");
|
||||||
|
peerConnectionClient.createPeerConnection(
|
||||||
|
localRender, remoteRender,
|
||||||
|
signalingParameters, peerConnectionParameters);
|
||||||
|
|
||||||
|
if (signalingParameters.initiator) {
|
||||||
|
logAndToast("Creating OFFER...");
|
||||||
|
// Create offer. Offer SDP will be sent to answering client in
|
||||||
|
// PeerConnectionEvents.onLocalDescription event.
|
||||||
|
peerConnectionClient.createOffer();
|
||||||
|
} else {
|
||||||
|
if (params.offerSdp != null) {
|
||||||
|
peerConnectionClient.setRemoteDescription(params.offerSdp);
|
||||||
|
logAndToast("Creating ANSWER...");
|
||||||
|
// Create answer. Answer SDP will be sent to offering client in
|
||||||
|
// PeerConnectionEvents.onLocalDescription event.
|
||||||
|
peerConnectionClient.createAnswer();
|
||||||
|
}
|
||||||
|
if (params.iceCandidates != null) {
|
||||||
|
// Add remote ICE candidates from room.
|
||||||
|
for (IceCandidate iceCandidate : params.iceCandidates) {
|
||||||
|
peerConnectionClient.addRemoteIceCandidate(iceCandidate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onConnectedToRoom(final SignalingParameters params) {
|
||||||
|
runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
onConnectedToRoomInternal(params);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRemoteDescription(final SessionDescription sdp) {
|
||||||
|
runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (peerConnectionClient == null) {
|
||||||
|
Log.e(TAG, "Received remote SDP for non-initilized peer connection.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
logAndToast("Received remote " + sdp.type + " ...");
|
||||||
|
peerConnectionClient.setRemoteDescription(sdp);
|
||||||
|
if (!signalingParameters.initiator) {
|
||||||
|
logAndToast("Creating ANSWER...");
|
||||||
|
// Create answer. Answer SDP will be sent to offering client in
|
||||||
|
// PeerConnectionEvents.onLocalDescription event.
|
||||||
|
peerConnectionClient.createAnswer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRemoteIceCandidate(final IceCandidate candidate) {
|
||||||
|
runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (peerConnectionClient == null) {
|
||||||
|
Log.e(TAG,
|
||||||
|
"Received ICE candidate for non-initilized peer connection.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
peerConnectionClient.addRemoteIceCandidate(candidate);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChannelClose() {
|
||||||
|
runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
logAndToast("Remote end hung up; dropping PeerConnection");
|
||||||
|
disconnect();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChannelError(final String description) {
|
||||||
|
runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (!isError) {
|
||||||
|
isError = true;
|
||||||
|
disconnectWithErrorMessage(description);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----Implementation of PeerConnectionClient.PeerConnectionEvents.---------
|
||||||
|
// Send local peer connection SDP and ICE candidates to remote party.
|
||||||
|
// All callbacks are invoked from peer connection client looper thread and
|
||||||
|
// are routed to UI thread.
|
||||||
|
@Override
|
||||||
|
public void onLocalDescription(final SessionDescription sdp) {
|
||||||
|
runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (appRtcClient != null) {
|
||||||
|
logAndToast("Sending " + sdp.type + " ...");
|
||||||
|
if (signalingParameters.initiator) {
|
||||||
|
appRtcClient.sendOfferSdp(sdp);
|
||||||
|
} else {
|
||||||
|
appRtcClient.sendAnswerSdp(sdp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onIceCandidate(final IceCandidate candidate) {
|
||||||
|
runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (appRtcClient != null) {
|
||||||
|
appRtcClient.sendLocalIceCandidate(candidate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onIceConnected() {
|
||||||
|
runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
logAndToast("ICE connected");
|
||||||
|
iceConnected = true;
|
||||||
|
callConnected();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onIceDisconnected() {
|
||||||
|
runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
logAndToast("ICE disconnected");
|
||||||
|
iceConnected = false;
|
||||||
|
disconnect();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPeerConnectionClosed() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPeerConnectionStatsReady(final StatsReport[] reports) {
|
||||||
|
runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (!isError && iceConnected) {
|
||||||
|
callFragment.updateEncoderStatistics(reports);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPeerConnectionError(final String description) {
|
||||||
|
runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (!isError) {
|
||||||
|
isError = true;
|
||||||
|
disconnectWithErrorMessage(description);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
230
talk/examples/android/src/org/appspot/apprtc/CallFragment.java
Normal file
230
talk/examples/android/src/org/appspot/apprtc/CallFragment.java
Normal file
@ -0,0 +1,230 @@
|
|||||||
|
/*
|
||||||
|
* 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.Activity;
|
||||||
|
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 org.webrtc.VideoRendererGui.ScalingType;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fragment for call control.
|
||||||
|
*/
|
||||||
|
public class CallFragment extends Fragment {
|
||||||
|
private View controlView;
|
||||||
|
private TextView encoderStatView;
|
||||||
|
private TextView roomIdView;
|
||||||
|
private ImageButton disconnectButton;
|
||||||
|
private ImageButton cameraSwitchButton;
|
||||||
|
private ImageButton videoScalingButton;
|
||||||
|
private ImageButton toggleDebugButton;
|
||||||
|
private OnCallEvents callEvents;
|
||||||
|
private ScalingType scalingType;
|
||||||
|
private boolean displayHud;
|
||||||
|
private volatile boolean isRunning;
|
||||||
|
private TextView hudView;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call control interface for container activity.
|
||||||
|
*/
|
||||||
|
public interface OnCallEvents {
|
||||||
|
public void onCallHangUp();
|
||||||
|
public void onCameraSwitch();
|
||||||
|
public void onVideoScalingSwitch(ScalingType scalingType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||||
|
Bundle savedInstanceState) {
|
||||||
|
controlView =
|
||||||
|
inflater.inflate(R.layout.fragment_call, container, false);
|
||||||
|
|
||||||
|
// Create UI controls.
|
||||||
|
encoderStatView =
|
||||||
|
(TextView) controlView.findViewById(R.id.encoder_stat_call);
|
||||||
|
roomIdView =
|
||||||
|
(TextView) controlView.findViewById(R.id.contact_name_call);
|
||||||
|
hudView =
|
||||||
|
(TextView) controlView.findViewById(R.id.hud_stat_call);
|
||||||
|
disconnectButton =
|
||||||
|
(ImageButton) controlView.findViewById(R.id.button_call_disconnect);
|
||||||
|
cameraSwitchButton =
|
||||||
|
(ImageButton) controlView.findViewById(R.id.button_call_switch_camera);
|
||||||
|
videoScalingButton =
|
||||||
|
(ImageButton) controlView.findViewById(R.id.button_call_scaling_mode);
|
||||||
|
toggleDebugButton =
|
||||||
|
(ImageButton) controlView.findViewById(R.id.button_toggle_debug);
|
||||||
|
|
||||||
|
// Add buttons click events.
|
||||||
|
disconnectButton.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
callEvents.onCallHangUp();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
cameraSwitchButton.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
callEvents.onCameraSwitch();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
videoScalingButton.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
if (scalingType == ScalingType.SCALE_ASPECT_FILL) {
|
||||||
|
videoScalingButton.setBackgroundResource(
|
||||||
|
R.drawable.ic_action_full_screen);
|
||||||
|
scalingType = ScalingType.SCALE_ASPECT_FIT;
|
||||||
|
} else {
|
||||||
|
videoScalingButton.setBackgroundResource(
|
||||||
|
R.drawable.ic_action_return_from_full_screen);
|
||||||
|
scalingType = ScalingType.SCALE_ASPECT_FILL;
|
||||||
|
}
|
||||||
|
callEvents.onVideoScalingSwitch(scalingType);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStart() {
|
||||||
|
super.onStart();
|
||||||
|
|
||||||
|
Bundle args = getArguments();
|
||||||
|
if (args != null) {
|
||||||
|
String roomId = args.getString(CallActivity.EXTRA_ROOMID);
|
||||||
|
roomIdView.setText(roomId);
|
||||||
|
displayHud = args.getBoolean(CallActivity.EXTRA_DISPLAY_HUD, false);
|
||||||
|
}
|
||||||
|
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
|
||||||
|
public void onAttach(Activity activity) {
|
||||||
|
super.onAttach(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");
|
||||||
|
}
|
||||||
|
encoderStatView.setText(stat.toString());
|
||||||
|
hudView.setText(bweBuilder.toString() + hudView.getText());
|
||||||
|
}
|
||||||
|
}
|
@ -52,7 +52,6 @@ import android.widget.TextView;
|
|||||||
|
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.webrtc.MediaCodecVideoEncoder;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
@ -61,14 +60,6 @@ import java.util.Random;
|
|||||||
* Handles the initial setup where the user selects which room to join.
|
* Handles the initial setup where the user selects which room to join.
|
||||||
*/
|
*/
|
||||||
public class ConnectActivity extends Activity {
|
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_VIDEOCODEC = "org.appspot.apprtc.VIDEOCODEC";
|
|
||||||
public static final String EXTRA_HWCODEC = "org.appspot.apprtc.HWCODEC";
|
|
||||||
private static final String TAG = "ConnectActivity";
|
private static final String TAG = "ConnectActivity";
|
||||||
private static final int CONNECTION_REQUEST = 1;
|
private static final int CONNECTION_REQUEST = 1;
|
||||||
private static boolean commandLineRun = false;
|
private static boolean commandLineRun = false;
|
||||||
@ -87,6 +78,7 @@ public class ConnectActivity extends Activity {
|
|||||||
private String keyprefVideoCodec;
|
private String keyprefVideoCodec;
|
||||||
private String keyprefHwCodecAcceleration;
|
private String keyprefHwCodecAcceleration;
|
||||||
private String keyprefCpuUsageDetection;
|
private String keyprefCpuUsageDetection;
|
||||||
|
private String keyprefDisplayHud;
|
||||||
private String keyprefRoomServerUrl;
|
private String keyprefRoomServerUrl;
|
||||||
private String keyprefRoom;
|
private String keyprefRoom;
|
||||||
private String keyprefRoomList;
|
private String keyprefRoomList;
|
||||||
@ -107,6 +99,7 @@ public class ConnectActivity extends Activity {
|
|||||||
keyprefVideoCodec = getString(R.string.pref_videocodec_key);
|
keyprefVideoCodec = getString(R.string.pref_videocodec_key);
|
||||||
keyprefHwCodecAcceleration = getString(R.string.pref_hwcodec_key);
|
keyprefHwCodecAcceleration = getString(R.string.pref_hwcodec_key);
|
||||||
keyprefCpuUsageDetection = getString(R.string.pref_cpu_usage_detection_key);
|
keyprefCpuUsageDetection = getString(R.string.pref_cpu_usage_detection_key);
|
||||||
|
keyprefDisplayHud = getString(R.string.pref_displayhud_key);
|
||||||
keyprefRoomServerUrl = getString(R.string.pref_room_server_url_key);
|
keyprefRoomServerUrl = getString(R.string.pref_room_server_url_key);
|
||||||
keyprefRoom = getString(R.string.pref_room_key);
|
keyprefRoom = getString(R.string.pref_room_key);
|
||||||
keyprefRoomList = getString(R.string.pref_room_list_key);
|
keyprefRoomList = getString(R.string.pref_room_list_key);
|
||||||
@ -146,8 +139,10 @@ public class ConnectActivity extends Activity {
|
|||||||
if ("android.intent.action.VIEW".equals(intent.getAction())
|
if ("android.intent.action.VIEW".equals(intent.getAction())
|
||||||
&& !commandLineRun) {
|
&& !commandLineRun) {
|
||||||
commandLineRun = true;
|
commandLineRun = true;
|
||||||
boolean loopback = intent.getBooleanExtra(EXTRA_LOOPBACK, false);
|
boolean loopback = intent.getBooleanExtra(
|
||||||
int runTimeMs = intent.getIntExtra(EXTRA_RUNTIME, 0);
|
CallActivity.EXTRA_LOOPBACK, false);
|
||||||
|
int runTimeMs = intent.getIntExtra(
|
||||||
|
CallActivity.EXTRA_RUNTIME, 0);
|
||||||
String room = sharedPref.getString(keyprefRoom, "");
|
String room = sharedPref.getString(keyprefRoom, "");
|
||||||
roomEditText.setText(room);
|
roomEditText.setText(room);
|
||||||
connectToRoom(loopback, runTimeMs);
|
connectToRoom(loopback, runTimeMs);
|
||||||
@ -216,6 +211,7 @@ public class ConnectActivity extends Activity {
|
|||||||
if (requestCode == CONNECTION_REQUEST && commandLineRun) {
|
if (requestCode == CONNECTION_REQUEST && commandLineRun) {
|
||||||
Log.d(TAG, "Return: " + resultCode);
|
Log.d(TAG, "Return: " + resultCode);
|
||||||
setResult(resultCode);
|
setResult(resultCode);
|
||||||
|
commandLineRun = false;
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -232,32 +228,21 @@ public class ConnectActivity extends Activity {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private String appendQueryParameter(String url, String parameter) {
|
|
||||||
String newUrl = url;
|
|
||||||
if (newUrl.contains("?")) {
|
|
||||||
newUrl += "&" + parameter;
|
|
||||||
} else {
|
|
||||||
newUrl += "?" + parameter;
|
|
||||||
}
|
|
||||||
return newUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void connectToRoom(boolean loopback, int runTimeMs) {
|
private void connectToRoom(boolean loopback, int runTimeMs) {
|
||||||
// Get room name (random for loopback).
|
// Get room name (random for loopback).
|
||||||
String roomName;
|
String roomId;
|
||||||
if (loopback) {
|
if (loopback) {
|
||||||
roomName = Integer.toString((new Random()).nextInt(100000000));
|
roomId = Integer.toString((new Random()).nextInt(100000000));
|
||||||
} else {
|
} else {
|
||||||
roomName = getSelectedItem();
|
roomId = getSelectedItem();
|
||||||
if (roomName == null) {
|
if (roomId == null) {
|
||||||
roomName = roomEditText.getText().toString();
|
roomId = roomEditText.getText().toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String url = sharedPref.getString(
|
String roomUrl = sharedPref.getString(
|
||||||
keyprefRoomServerUrl,
|
keyprefRoomServerUrl,
|
||||||
getString(R.string.pref_room_server_url_default));
|
getString(R.string.pref_room_server_url_default));
|
||||||
url = WebSocketRTCClient.getGAEConnectionUrl(url, roomName);
|
|
||||||
|
|
||||||
// Get default video codec.
|
// Get default video codec.
|
||||||
String videoCodec = sharedPref.getString(keyprefVideoCodec,
|
String videoCodec = sharedPref.getString(keyprefVideoCodec,
|
||||||
@ -267,60 +252,36 @@ public class ConnectActivity extends Activity {
|
|||||||
boolean hwCodec = sharedPref.getBoolean(keyprefHwCodecAcceleration,
|
boolean hwCodec = sharedPref.getBoolean(keyprefHwCodecAcceleration,
|
||||||
Boolean.valueOf(getString(R.string.pref_hwcodec_default)));
|
Boolean.valueOf(getString(R.string.pref_hwcodec_default)));
|
||||||
|
|
||||||
// Add video resolution constraints.
|
// Get video resolution from settings.
|
||||||
String parametersResolution = null;
|
int videoWidth = 0;
|
||||||
String parametersFps = null;
|
int videoHeight = 0;
|
||||||
String resolution = sharedPref.getString(keyprefResolution,
|
String resolution = sharedPref.getString(keyprefResolution,
|
||||||
getString(R.string.pref_resolution_default));
|
getString(R.string.pref_resolution_default));
|
||||||
String[] dimensions = resolution.split("[ x]+");
|
String[] dimensions = resolution.split("[ x]+");
|
||||||
if (dimensions.length == 2) {
|
if (dimensions.length == 2) {
|
||||||
try {
|
try {
|
||||||
int maxWidth = Integer.parseInt(dimensions[0]);
|
videoWidth = Integer.parseInt(dimensions[0]);
|
||||||
int maxHeight = Integer.parseInt(dimensions[1]);
|
videoHeight = Integer.parseInt(dimensions[1]);
|
||||||
if (maxWidth > 0 && maxHeight > 0) {
|
|
||||||
parametersResolution = "minHeight=" + maxHeight + ",maxHeight="
|
|
||||||
+ maxHeight + ",minWidth=" + maxWidth + ",maxWidth=" + maxWidth;
|
|
||||||
}
|
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
|
videoWidth = 0;
|
||||||
|
videoHeight = 0;
|
||||||
Log.e(TAG, "Wrong video resolution setting: " + resolution);
|
Log.e(TAG, "Wrong video resolution setting: " + resolution);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add camera fps constraints.
|
// Get camera fps from settings.
|
||||||
|
int cameraFps = 0;
|
||||||
String fps = sharedPref.getString(keyprefFps,
|
String fps = sharedPref.getString(keyprefFps,
|
||||||
getString(R.string.pref_fps_default));
|
getString(R.string.pref_fps_default));
|
||||||
String[] fpsValues = fps.split("[ x]+");
|
String[] fpsValues = fps.split("[ x]+");
|
||||||
if (fpsValues.length == 2) {
|
if (fpsValues.length == 2) {
|
||||||
try {
|
try {
|
||||||
int cameraFps = Integer.parseInt(fpsValues[0]);
|
cameraFps = Integer.parseInt(fpsValues[0]);
|
||||||
if (cameraFps > 0) {
|
|
||||||
parametersFps = "minFrameRate=" + cameraFps
|
|
||||||
+ ",maxFrameRate=" + cameraFps;
|
|
||||||
}
|
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
Log.e(TAG, "Wrong camera fps setting: " + fps);
|
Log.e(TAG, "Wrong camera fps setting: " + fps);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Modify connection URL.
|
|
||||||
if (parametersResolution != null || parametersFps != null) {
|
|
||||||
String urlVideoParameters = "video=";
|
|
||||||
if (parametersResolution != null) {
|
|
||||||
urlVideoParameters += parametersResolution;
|
|
||||||
if (parametersFps != null) {
|
|
||||||
urlVideoParameters += ",";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (parametersFps != null) {
|
|
||||||
urlVideoParameters += parametersFps;
|
|
||||||
}
|
|
||||||
url = appendQueryParameter(url, urlVideoParameters);
|
|
||||||
} else {
|
|
||||||
if (hwCodec && MediaCodecVideoEncoder.isPlatformSupported()) {
|
|
||||||
url = appendQueryParameter(url, "hd=true");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get start bitrate.
|
// Get start bitrate.
|
||||||
int startBitrate = 0;
|
int startBitrate = 0;
|
||||||
String bitrateTypeDefault = getString(R.string.pref_startbitrate_default);
|
String bitrateTypeDefault = getString(R.string.pref_startbitrate_default);
|
||||||
@ -337,23 +298,31 @@ public class ConnectActivity extends Activity {
|
|||||||
keyprefCpuUsageDetection,
|
keyprefCpuUsageDetection,
|
||||||
Boolean.valueOf(
|
Boolean.valueOf(
|
||||||
getString(R.string.pref_cpu_usage_detection_default)));
|
getString(R.string.pref_cpu_usage_detection_default)));
|
||||||
if (!cpuOveruseDetection) {
|
|
||||||
url = appendQueryParameter(url, "googCpuOveruseDetection=false");
|
// Check statistics display option.
|
||||||
}
|
boolean displayHud = sharedPref.getBoolean(keyprefDisplayHud,
|
||||||
|
Boolean.valueOf(getString(R.string.pref_displayhud_default)));
|
||||||
|
|
||||||
// Start AppRTCDemo activity.
|
// Start AppRTCDemo activity.
|
||||||
Log.d(TAG, "Connecting to room " + roomName + " at URL " + url);
|
Log.d(TAG, "Connecting to room " + roomId + " at URL " + roomUrl);
|
||||||
if (validateUrl(url)) {
|
if (validateUrl(roomUrl)) {
|
||||||
Uri uri = Uri.parse(url);
|
Uri uri = Uri.parse(roomUrl);
|
||||||
Intent intent = new Intent(this, AppRTCDemoActivity.class);
|
Intent intent = new Intent(this, CallActivity.class);
|
||||||
intent.setData(uri);
|
intent.setData(uri);
|
||||||
intent.putExtra(EXTRA_ROOMNAME, roomName);
|
intent.putExtra(CallActivity.EXTRA_ROOMID, roomId);
|
||||||
intent.putExtra(EXTRA_LOOPBACK, loopback);
|
intent.putExtra(CallActivity.EXTRA_LOOPBACK, loopback);
|
||||||
intent.putExtra(EXTRA_CMDLINE, commandLineRun);
|
intent.putExtra(CallActivity.EXTRA_VIDEOCODEC, videoCodec);
|
||||||
intent.putExtra(EXTRA_RUNTIME, runTimeMs);
|
intent.putExtra(CallActivity.EXTRA_HWCODEC, hwCodec);
|
||||||
intent.putExtra(EXTRA_BITRATE, startBitrate);
|
intent.putExtra(CallActivity.EXTRA_VIDEO_BITRATE, startBitrate);
|
||||||
intent.putExtra(EXTRA_VIDEOCODEC, videoCodec);
|
intent.putExtra(CallActivity.EXTRA_VIDEO_WIDTH, videoWidth);
|
||||||
intent.putExtra(EXTRA_HWCODEC, hwCodec);
|
intent.putExtra(CallActivity.EXTRA_VIDEO_HEIGHT, videoHeight);
|
||||||
|
intent.putExtra(CallActivity.EXTRA_VIDEO_FPS, cameraFps);
|
||||||
|
intent.putExtra(CallActivity.EXTRA_CPUOVERUSE_DETECTION,
|
||||||
|
cpuOveruseDetection);
|
||||||
|
intent.putExtra(CallActivity.EXTRA_DISPLAY_HUD, displayHud);
|
||||||
|
intent.putExtra(CallActivity.EXTRA_CMDLINE, commandLineRun);
|
||||||
|
intent.putExtra(CallActivity.EXTRA_RUNTIME, runTimeMs);
|
||||||
|
|
||||||
startActivityForResult(intent, CONNECTION_REQUEST);
|
startActivityForResult(intent, CONNECTION_REQUEST);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,21 +36,25 @@ import android.util.Log;
|
|||||||
|
|
||||||
import org.webrtc.DataChannel;
|
import org.webrtc.DataChannel;
|
||||||
import org.webrtc.IceCandidate;
|
import org.webrtc.IceCandidate;
|
||||||
|
import org.webrtc.MediaCodecVideoEncoder;
|
||||||
import org.webrtc.MediaConstraints;
|
import org.webrtc.MediaConstraints;
|
||||||
|
import org.webrtc.MediaConstraints.KeyValuePair;
|
||||||
import org.webrtc.MediaStream;
|
import org.webrtc.MediaStream;
|
||||||
import org.webrtc.MediaStreamTrack;
|
|
||||||
import org.webrtc.PeerConnection;
|
import org.webrtc.PeerConnection;
|
||||||
import org.webrtc.PeerConnection.IceConnectionState;
|
import org.webrtc.PeerConnection.IceConnectionState;
|
||||||
import org.webrtc.PeerConnectionFactory;
|
import org.webrtc.PeerConnectionFactory;
|
||||||
import org.webrtc.SdpObserver;
|
import org.webrtc.SdpObserver;
|
||||||
import org.webrtc.SessionDescription;
|
import org.webrtc.SessionDescription;
|
||||||
import org.webrtc.StatsObserver;
|
import org.webrtc.StatsObserver;
|
||||||
|
import org.webrtc.StatsReport;
|
||||||
import org.webrtc.VideoCapturer;
|
import org.webrtc.VideoCapturer;
|
||||||
import org.webrtc.VideoRenderer;
|
import org.webrtc.VideoRenderer;
|
||||||
import org.webrtc.VideoSource;
|
import org.webrtc.VideoSource;
|
||||||
import org.webrtc.VideoTrack;
|
import org.webrtc.VideoTrack;
|
||||||
|
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
|
import java.util.Timer;
|
||||||
|
import java.util.TimerTask;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
@ -61,39 +65,77 @@ import java.util.regex.Pattern;
|
|||||||
* All PeerConnectionEvents callbacks are invoked from the same looper thread.
|
* All PeerConnectionEvents callbacks are invoked from the same looper thread.
|
||||||
*/
|
*/
|
||||||
public class PeerConnectionClient {
|
public class PeerConnectionClient {
|
||||||
|
public static final String VIDEO_TRACK_ID = "ARDAMSv0";
|
||||||
|
public static final String AUDIO_TRACK_ID = "ARDAMSa0";
|
||||||
private static final String TAG = "PCRTCClient";
|
private static final String TAG = "PCRTCClient";
|
||||||
private static final boolean PREFER_ISAC = false;
|
private static final boolean PREFER_ISAC = false;
|
||||||
public static final String VIDEO_CODEC_VP8 = "VP8";
|
public static final String VIDEO_CODEC_VP8 = "VP8";
|
||||||
public static final String VIDEO_CODEC_VP9 = "VP9";
|
public static final String VIDEO_CODEC_VP9 = "VP9";
|
||||||
private static final String FIELD_TRIAL_VP9 = "WebRTC-SupportVP9/Enabled/";
|
private static final String FIELD_TRIAL_VP9 = "WebRTC-SupportVP9/Enabled/";
|
||||||
public static final String VIDEO_TRACK_ID = "ARDAMSv0";
|
private static final String MAX_VIDEO_WIDTH_CONSTRAINT = "maxWidth";
|
||||||
public static final String AUDIO_TRACK_ID = "ARDAMSa0";
|
private static final String MIN_VIDEO_WIDTH_CONSTRAINT = "minWidth";
|
||||||
|
private static final String MAX_VIDEO_HEIGHT_CONSTRAINT = "maxHeight";
|
||||||
|
private static final String MIN_VIDEO_HEIGHT_CONSTRAINT = "minHeight";
|
||||||
|
private static final String MAX_VIDEO_FPS_CONSTRAINT = "maxFrameRate";
|
||||||
|
private static final String MIN_VIDEO_FPS_CONSTRAINT = "minFrameRate";
|
||||||
|
private static final int HD_VIDEO_WIDTH = 1280;
|
||||||
|
private static final int HD_VIDEO_HEIGHT = 720;
|
||||||
|
private static final int MAX_VIDEO_WIDTH = 1280;
|
||||||
|
private static final int MAX_VIDEO_HEIGHT = 1280;
|
||||||
|
private static final int MAX_VIDEO_FPS = 30;
|
||||||
|
|
||||||
private final LooperExecutor executor;
|
private final LooperExecutor executor;
|
||||||
private PeerConnectionFactory factory = null;
|
private PeerConnectionFactory factory = null;
|
||||||
private PeerConnection pc = null;
|
private PeerConnection peerConnection = null;
|
||||||
private VideoSource videoSource;
|
private VideoSource videoSource;
|
||||||
private boolean videoSourceStopped = false;
|
private boolean videoSourceStopped = false;
|
||||||
private boolean isError = false;
|
private boolean isError = false;
|
||||||
|
private boolean videoCodecHwAcceleration;
|
||||||
|
private final Timer statsTimer = new Timer();
|
||||||
private final PCObserver pcObserver = new PCObserver();
|
private final PCObserver pcObserver = new PCObserver();
|
||||||
private final SDPObserver sdpObserver = new SDPObserver();
|
private final SDPObserver sdpObserver = new SDPObserver();
|
||||||
private VideoRenderer.Callbacks localRender;
|
private VideoRenderer.Callbacks localRender;
|
||||||
private VideoRenderer.Callbacks remoteRender;
|
private VideoRenderer.Callbacks remoteRender;
|
||||||
private SignalingParameters signalingParameters;
|
private SignalingParameters signalingParameters;
|
||||||
|
private MediaConstraints videoConstraints;
|
||||||
|
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 MediaConstraints sdpMediaConstraints;
|
||||||
private PeerConnectionEvents events;
|
private PeerConnectionEvents events;
|
||||||
private int startBitrate;
|
|
||||||
private boolean isInitiator;
|
private boolean isInitiator;
|
||||||
private boolean useFrontFacingCamera = true;
|
private boolean useFrontFacingCamera = true;
|
||||||
private SessionDescription localSdp = null; // either offer or answer SDP
|
private SessionDescription localSdp = null; // either offer or answer SDP
|
||||||
private MediaStream mediaStream = null;
|
private MediaStream mediaStream = null;
|
||||||
|
// enableVideo is set to true if video should be rendered and sent.
|
||||||
|
private boolean renderVideo = true;
|
||||||
|
private VideoTrack localVideoTrack = null;
|
||||||
|
private VideoTrack remoteVideoTrack = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SDP/ICE ready callbacks.
|
* Peer connection parameters.
|
||||||
|
*/
|
||||||
|
public static class PeerConnectionParameters {
|
||||||
|
public final int videoWidth;
|
||||||
|
public final int videoHeight;
|
||||||
|
public final int videoFps;
|
||||||
|
public final int videoStartBitrate;
|
||||||
|
public final boolean cpuOveruseDetection;
|
||||||
|
|
||||||
|
public PeerConnectionParameters(int videoWidth, int videoHeight,
|
||||||
|
int videoFps, int videoStartBitrate, boolean cpuOveruseDetection) {
|
||||||
|
this.videoWidth = videoWidth;
|
||||||
|
this.videoHeight = videoHeight;
|
||||||
|
this.videoFps = videoFps;
|
||||||
|
this.videoStartBitrate = videoStartBitrate;
|
||||||
|
this.cpuOveruseDetection = cpuOveruseDetection;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Peer connection events.
|
||||||
*/
|
*/
|
||||||
public static interface PeerConnectionEvents {
|
public static interface PeerConnectionEvents {
|
||||||
/**
|
/**
|
||||||
@ -123,11 +165,15 @@ public class PeerConnectionClient {
|
|||||||
*/
|
*/
|
||||||
public void onPeerConnectionClosed();
|
public void onPeerConnectionClosed();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback fired once peer connection statistics is ready.
|
||||||
|
*/
|
||||||
|
public void onPeerConnectionStatsReady(final StatsReport[] reports);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callback fired once peer connection error happened.
|
* Callback fired once peer connection error happened.
|
||||||
*/
|
*/
|
||||||
public void onPeerConnectionError(final String description);
|
public void onPeerConnectionError(final String description);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public PeerConnectionClient() {
|
public PeerConnectionClient() {
|
||||||
@ -141,12 +187,13 @@ public class PeerConnectionClient {
|
|||||||
final EGLContext renderEGLContext,
|
final EGLContext renderEGLContext,
|
||||||
final PeerConnectionEvents events) {
|
final PeerConnectionEvents events) {
|
||||||
this.events = events;
|
this.events = events;
|
||||||
|
this.videoCodecHwAcceleration = videoCodecHwAcceleration;
|
||||||
executor.requestStart();
|
executor.requestStart();
|
||||||
executor.execute(new Runnable() {
|
executor.execute(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
createPeerConnectionFactoryInternal(
|
createPeerConnectionFactoryInternal(
|
||||||
context, videoCodec, videoCodecHwAcceleration, renderEGLContext);
|
context, videoCodec, renderEGLContext);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -155,11 +202,51 @@ public class PeerConnectionClient {
|
|||||||
final VideoRenderer.Callbacks localRender,
|
final VideoRenderer.Callbacks localRender,
|
||||||
final VideoRenderer.Callbacks remoteRender,
|
final VideoRenderer.Callbacks remoteRender,
|
||||||
final SignalingParameters signalingParameters,
|
final SignalingParameters signalingParameters,
|
||||||
final int startBitrate) {
|
final PeerConnectionParameters peerConnectionParameters) {
|
||||||
this.localRender = localRender;
|
this.localRender = localRender;
|
||||||
this.remoteRender = remoteRender;
|
this.remoteRender = remoteRender;
|
||||||
this.signalingParameters = signalingParameters;
|
this.signalingParameters = signalingParameters;
|
||||||
this.startBitrate = startBitrate;
|
this.peerConnectionParameters = peerConnectionParameters;
|
||||||
|
// Merge video constraints from signaling parameters and peer connection
|
||||||
|
// parameters.
|
||||||
|
videoConstraints = signalingParameters.videoConstraints;
|
||||||
|
if (videoConstraints != null && peerConnectionParameters != null) {
|
||||||
|
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) && videoCodecHwAcceleration &&
|
||||||
|
MediaCodecVideoEncoder.isPlatformSupported()) {
|
||||||
|
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() {
|
||||||
@ -181,7 +268,6 @@ public class PeerConnectionClient {
|
|||||||
private void createPeerConnectionFactoryInternal(
|
private void createPeerConnectionFactoryInternal(
|
||||||
Context context,
|
Context context,
|
||||||
String videoCodec,
|
String videoCodec,
|
||||||
boolean videoCodecHwAcceleration,
|
|
||||||
EGLContext renderEGLContext) {
|
EGLContext renderEGLContext) {
|
||||||
Log.d(TAG, "Create peer connection factory with EGLContext "
|
Log.d(TAG, "Create peer connection factory with EGLContext "
|
||||||
+ renderEGLContext);
|
+ renderEGLContext);
|
||||||
@ -204,7 +290,8 @@ public class PeerConnectionClient {
|
|||||||
Log.e(TAG, "Peerconnection factory is not created");
|
Log.e(TAG, "Peerconnection factory is not created");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Log.d(TAG, "Create peer connection.");
|
Log.d(TAG, "Create peer connection. VideoConstraints: "
|
||||||
|
+ videoConstraints.toString());
|
||||||
isInitiator = signalingParameters.initiator;
|
isInitiator = signalingParameters.initiator;
|
||||||
queuedRemoteCandidates = new LinkedList<IceCandidate>();
|
queuedRemoteCandidates = new LinkedList<IceCandidate>();
|
||||||
|
|
||||||
@ -217,8 +304,8 @@ public class PeerConnectionClient {
|
|||||||
MediaConstraints pcConstraints = signalingParameters.pcConstraints;
|
MediaConstraints pcConstraints = signalingParameters.pcConstraints;
|
||||||
pcConstraints.optional.add(
|
pcConstraints.optional.add(
|
||||||
new MediaConstraints.KeyValuePair("RtpDataChannels", "true"));
|
new MediaConstraints.KeyValuePair("RtpDataChannels", "true"));
|
||||||
pc = factory.createPeerConnection(signalingParameters.iceServers,
|
peerConnection = factory.createPeerConnection(
|
||||||
pcConstraints, pcObserver);
|
signalingParameters.iceServers, pcConstraints, pcObserver);
|
||||||
isInitiator = false;
|
isInitiator = false;
|
||||||
|
|
||||||
// Uncomment to get ALL WebRTC tracing and SENSITIVE libjingle logging.
|
// Uncomment to get ALL WebRTC tracing and SENSITIVE libjingle logging.
|
||||||
@ -229,7 +316,7 @@ public class PeerConnectionClient {
|
|||||||
// Logging.Severity.LS_SENSITIVE);
|
// Logging.Severity.LS_SENSITIVE);
|
||||||
|
|
||||||
mediaStream = factory.createLocalMediaStream("ARDAMS");
|
mediaStream = factory.createLocalMediaStream("ARDAMS");
|
||||||
if (signalingParameters.videoConstraints != null) {
|
if (videoConstraints != null) {
|
||||||
mediaStream.addTrack(createVideoTrack(useFrontFacingCamera));
|
mediaStream.addTrack(createVideoTrack(useFrontFacingCamera));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -238,15 +325,17 @@ public class PeerConnectionClient {
|
|||||||
AUDIO_TRACK_ID,
|
AUDIO_TRACK_ID,
|
||||||
factory.createAudioSource(signalingParameters.audioConstraints)));
|
factory.createAudioSource(signalingParameters.audioConstraints)));
|
||||||
}
|
}
|
||||||
pc.addStream(mediaStream);
|
peerConnection.addStream(mediaStream);
|
||||||
|
|
||||||
Log.d(TAG, "Peer connection created.");
|
Log.d(TAG, "Peer connection created.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void closeInternal() {
|
private void closeInternal() {
|
||||||
Log.d(TAG, "Closing peer connection.");
|
Log.d(TAG, "Closing peer connection.");
|
||||||
if (pc != null) {
|
statsTimer.cancel();
|
||||||
pc.dispose();
|
if (peerConnection != null) {
|
||||||
pc = null;
|
peerConnection.dispose();
|
||||||
|
peerConnection = null;
|
||||||
}
|
}
|
||||||
if (videoSource != null) {
|
if (videoSource != null) {
|
||||||
videoSource.dispose();
|
videoSource.dispose();
|
||||||
@ -261,22 +350,90 @@ public class PeerConnectionClient {
|
|||||||
events.onPeerConnectionClosed();
|
events.onPeerConnectionClosed();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean getStats(StatsObserver observer, MediaStreamTrack track) {
|
public boolean isHDVideo() {
|
||||||
if (pc != null && !isError) {
|
if (videoConstraints == null) {
|
||||||
return pc.getStats(observer, track);
|
return false;
|
||||||
|
}
|
||||||
|
int minWidth = 0;
|
||||||
|
int minHeight = 0;
|
||||||
|
for (KeyValuePair keyValuePair : videoConstraints.mandatory) {
|
||||||
|
if (keyValuePair.getKey().equals("minWidth")) {
|
||||||
|
try {
|
||||||
|
minWidth = Integer.parseInt(keyValuePair.getValue());
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
Log.e(TAG, "Can not parse video width from video constraints");
|
||||||
|
}
|
||||||
|
} else if (keyValuePair.getKey().equals("minHeight")) {
|
||||||
|
try {
|
||||||
|
minHeight = Integer.parseInt(keyValuePair.getValue());
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
Log.e(TAG, "Can not parse video height from video constraints");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (minWidth * minHeight >= 1280 * 720) {
|
||||||
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void getStats() {
|
||||||
|
if (peerConnection == null || isError) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
boolean success = peerConnection.getStats(new StatsObserver() {
|
||||||
|
@Override
|
||||||
|
public void onComplete(final StatsReport[] reports) {
|
||||||
|
events.onPeerConnectionStatsReady(reports);
|
||||||
|
}
|
||||||
|
}, null);
|
||||||
|
if (!success) {
|
||||||
|
Log.e(TAG, "getStats() returns false!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void enableStatsEvents(boolean enable, int periodMs) {
|
||||||
|
if (enable) {
|
||||||
|
statsTimer.schedule(new TimerTask() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
executor.execute(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
getStats();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, 0, periodMs);
|
||||||
|
} else {
|
||||||
|
statsTimer.cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVideoEnabled(final boolean enable) {
|
||||||
|
executor.execute(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
renderVideo = enable;
|
||||||
|
if (localVideoTrack != null) {
|
||||||
|
localVideoTrack.setEnabled(renderVideo);
|
||||||
|
}
|
||||||
|
if (remoteVideoTrack != null) {
|
||||||
|
remoteVideoTrack.setEnabled(renderVideo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public void createOffer() {
|
public void createOffer() {
|
||||||
executor.execute(new Runnable() {
|
executor.execute(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
if (pc != null && !isError) {
|
if (peerConnection != null && !isError) {
|
||||||
Log.d(TAG, "PC Create OFFER");
|
Log.d(TAG, "PC Create OFFER");
|
||||||
isInitiator = true;
|
isInitiator = true;
|
||||||
pc.createOffer(sdpObserver, sdpMediaConstraints);
|
peerConnection.createOffer(sdpObserver, sdpMediaConstraints);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -286,10 +443,10 @@ public class PeerConnectionClient {
|
|||||||
executor.execute(new Runnable() {
|
executor.execute(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
if (pc != null && !isError) {
|
if (peerConnection != null && !isError) {
|
||||||
Log.d(TAG, "PC create ANSWER");
|
Log.d(TAG, "PC create ANSWER");
|
||||||
isInitiator = false;
|
isInitiator = false;
|
||||||
pc.createAnswer(sdpObserver, sdpMediaConstraints);
|
peerConnection.createAnswer(sdpObserver, sdpMediaConstraints);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -299,11 +456,11 @@ public class PeerConnectionClient {
|
|||||||
executor.execute(new Runnable() {
|
executor.execute(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
if (pc != null && !isError) {
|
if (peerConnection != null && !isError) {
|
||||||
if (queuedRemoteCandidates != null) {
|
if (queuedRemoteCandidates != null) {
|
||||||
queuedRemoteCandidates.add(candidate);
|
queuedRemoteCandidates.add(candidate);
|
||||||
} else {
|
} else {
|
||||||
pc.addIceCandidate(candidate);
|
peerConnection.addIceCandidate(candidate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -314,20 +471,21 @@ public class PeerConnectionClient {
|
|||||||
executor.execute(new Runnable() {
|
executor.execute(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
if (pc == null || isError) {
|
if (peerConnection == null || isError) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String sdpDescription = sdp.description;
|
String sdpDescription = sdp.description;
|
||||||
if (PREFER_ISAC) {
|
if (PREFER_ISAC) {
|
||||||
sdpDescription = preferISAC(sdpDescription);
|
sdpDescription = preferISAC(sdpDescription);
|
||||||
}
|
}
|
||||||
if (startBitrate > 0) {
|
if (peerConnectionParameters.videoStartBitrate > 0) {
|
||||||
sdpDescription = setStartBitrate(sdpDescription, startBitrate);
|
sdpDescription = setStartBitrate(sdpDescription,
|
||||||
|
peerConnectionParameters.videoStartBitrate);
|
||||||
}
|
}
|
||||||
Log.d(TAG, "Set remote SDP.");
|
Log.d(TAG, "Set remote SDP.");
|
||||||
SessionDescription sdpRemote = new SessionDescription(
|
SessionDescription sdpRemote = new SessionDescription(
|
||||||
sdp.type, sdpDescription);
|
sdp.type, sdpDescription);
|
||||||
pc.setRemoteDescription(sdpObserver, sdpRemote);
|
peerConnection.setRemoteDescription(sdpObserver, sdpRemote);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -405,13 +563,14 @@ public class PeerConnectionClient {
|
|||||||
videoSource.dispose();
|
videoSource.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
videoSource = factory.createVideoSource(
|
videoSource = factory.createVideoSource(capturer, videoConstraints);
|
||||||
capturer, signalingParameters.videoConstraints);
|
|
||||||
String trackExtension = frontFacing ? "frontFacing" : "backFacing";
|
String trackExtension = frontFacing ? "frontFacing" : "backFacing";
|
||||||
VideoTrack videoTrack =
|
|
||||||
|
localVideoTrack =
|
||||||
factory.createVideoTrack(VIDEO_TRACK_ID + trackExtension, videoSource);
|
factory.createVideoTrack(VIDEO_TRACK_ID + trackExtension, videoSource);
|
||||||
videoTrack.addRenderer(new VideoRenderer(localRender));
|
localVideoTrack.setEnabled(renderVideo);
|
||||||
return videoTrack;
|
localVideoTrack.addRenderer(new VideoRenderer(localRender));
|
||||||
|
return localVideoTrack;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String setStartBitrate(
|
private static String setStartBitrate(
|
||||||
@ -500,23 +659,24 @@ public class PeerConnectionClient {
|
|||||||
if (queuedRemoteCandidates != null) {
|
if (queuedRemoteCandidates != null) {
|
||||||
Log.d(TAG, "Add " + queuedRemoteCandidates.size() + " remote candidates");
|
Log.d(TAG, "Add " + queuedRemoteCandidates.size() + " remote candidates");
|
||||||
for (IceCandidate candidate : queuedRemoteCandidates) {
|
for (IceCandidate candidate : queuedRemoteCandidates) {
|
||||||
pc.addIceCandidate(candidate);
|
peerConnection.addIceCandidate(candidate);
|
||||||
}
|
}
|
||||||
queuedRemoteCandidates = null;
|
queuedRemoteCandidates = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void switchCameraInternal() {
|
private void switchCameraInternal() {
|
||||||
if (signalingParameters.videoConstraints == null) {
|
if (videoConstraints == null) {
|
||||||
return; // No video is sent.
|
return; // No video is sent.
|
||||||
}
|
}
|
||||||
if (pc.signalingState() != PeerConnection.SignalingState.STABLE) {
|
if (peerConnection.signalingState()
|
||||||
|
!= PeerConnection.SignalingState.STABLE) {
|
||||||
Log.e(TAG, "Switching camera during negotiation is not handled.");
|
Log.e(TAG, "Switching camera during negotiation is not handled.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.d(TAG, "Switch camera");
|
Log.d(TAG, "Switch camera");
|
||||||
pc.removeStream(mediaStream);
|
peerConnection.removeStream(mediaStream);
|
||||||
VideoTrack currentTrack = mediaStream.videoTracks.get(0);
|
VideoTrack currentTrack = mediaStream.videoTracks.get(0);
|
||||||
mediaStream.removeTrack(currentTrack);
|
mediaStream.removeTrack(currentTrack);
|
||||||
|
|
||||||
@ -530,9 +690,9 @@ public class PeerConnectionClient {
|
|||||||
useFrontFacingCamera = !useFrontFacingCamera;
|
useFrontFacingCamera = !useFrontFacingCamera;
|
||||||
VideoTrack newTrack = createVideoTrack(useFrontFacingCamera);
|
VideoTrack newTrack = createVideoTrack(useFrontFacingCamera);
|
||||||
mediaStream.addTrack(newTrack);
|
mediaStream.addTrack(newTrack);
|
||||||
pc.addStream(mediaStream);
|
peerConnection.addStream(mediaStream);
|
||||||
|
|
||||||
SessionDescription remoteDesc = pc.getRemoteDescription();
|
SessionDescription remoteDesc = peerConnection.getRemoteDescription();
|
||||||
if (localSdp == null || remoteDesc == null) {
|
if (localSdp == null || remoteDesc == null) {
|
||||||
Log.d(TAG, "Switching camera before the negotiation started.");
|
Log.d(TAG, "Switching camera before the negotiation started.");
|
||||||
return;
|
return;
|
||||||
@ -542,11 +702,15 @@ public class PeerConnectionClient {
|
|||||||
localSdp.description.replaceAll(trackId, newTrack.id()));
|
localSdp.description.replaceAll(trackId, newTrack.id()));
|
||||||
|
|
||||||
if (isInitiator) {
|
if (isInitiator) {
|
||||||
pc.setLocalDescription(new SwitchCameraSdbObserver(), localSdp);
|
peerConnection.setLocalDescription(
|
||||||
pc.setRemoteDescription(new SwitchCameraSdbObserver(), remoteDesc);
|
new SwitchCameraSdbObserver(), localSdp);
|
||||||
|
peerConnection.setRemoteDescription(
|
||||||
|
new SwitchCameraSdbObserver(), remoteDesc);
|
||||||
} else {
|
} else {
|
||||||
pc.setRemoteDescription(new SwitchCameraSdbObserver(), remoteDesc);
|
peerConnection.setRemoteDescription(
|
||||||
pc.setLocalDescription(new SwitchCameraSdbObserver(), localSdp);
|
new SwitchCameraSdbObserver(), remoteDesc);
|
||||||
|
peerConnection.setLocalDescription(
|
||||||
|
new SwitchCameraSdbObserver(), localSdp);
|
||||||
}
|
}
|
||||||
Log.d(TAG, "Switch camera done");
|
Log.d(TAG, "Switch camera done");
|
||||||
}
|
}
|
||||||
@ -555,7 +719,7 @@ public class PeerConnectionClient {
|
|||||||
executor.execute(new Runnable() {
|
executor.execute(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
if (pc != null && !isError) {
|
if (peerConnection != null && !isError) {
|
||||||
switchCameraInternal();
|
switchCameraInternal();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -609,7 +773,7 @@ public class PeerConnectionClient {
|
|||||||
executor.execute(new Runnable() {
|
executor.execute(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
if (pc == null || isError) {
|
if (peerConnection == null || isError) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (stream.audioTracks.size() > 1 || stream.videoTracks.size() > 1) {
|
if (stream.audioTracks.size() > 1 || stream.videoTracks.size() > 1) {
|
||||||
@ -617,8 +781,9 @@ public class PeerConnectionClient {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (stream.videoTracks.size() == 1) {
|
if (stream.videoTracks.size() == 1) {
|
||||||
stream.videoTracks.get(0).addRenderer(
|
remoteVideoTrack = stream.videoTracks.get(0);
|
||||||
new VideoRenderer(remoteRender));
|
remoteVideoTrack.setEnabled(renderVideo);
|
||||||
|
remoteVideoTrack.addRenderer(new VideoRenderer(remoteRender));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -629,9 +794,10 @@ public class PeerConnectionClient {
|
|||||||
executor.execute(new Runnable() {
|
executor.execute(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
if (pc == null || isError) {
|
if (peerConnection == null || isError) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
remoteVideoTrack = null;
|
||||||
stream.videoTracks.get(0).dispose();
|
stream.videoTracks.get(0).dispose();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -669,9 +835,9 @@ public class PeerConnectionClient {
|
|||||||
executor.execute(new Runnable() {
|
executor.execute(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
if (pc != null && !isError) {
|
if (peerConnection != null && !isError) {
|
||||||
Log.d(TAG, "Set local SDP from " + sdp.type);
|
Log.d(TAG, "Set local SDP from " + sdp.type);
|
||||||
pc.setLocalDescription(sdpObserver, sdp);
|
peerConnection.setLocalDescription(sdpObserver, sdp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -682,13 +848,13 @@ public class PeerConnectionClient {
|
|||||||
executor.execute(new Runnable() {
|
executor.execute(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
if (pc == null || isError) {
|
if (peerConnection == null || isError) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (isInitiator) {
|
if (isInitiator) {
|
||||||
// For offering peer connection we first create offer and set
|
// For offering peer connection we first create offer and set
|
||||||
// local SDP, then after receiving answer set remote SDP.
|
// local SDP, then after receiving answer set remote SDP.
|
||||||
if (pc.getRemoteDescription() == null) {
|
if (peerConnection.getRemoteDescription() == null) {
|
||||||
// We've just set our local SDP so time to send it.
|
// We've just set our local SDP so time to send it.
|
||||||
Log.d(TAG, "Local SDP set succesfully");
|
Log.d(TAG, "Local SDP set succesfully");
|
||||||
events.onLocalDescription(localSdp);
|
events.onLocalDescription(localSdp);
|
||||||
@ -701,7 +867,7 @@ public class PeerConnectionClient {
|
|||||||
} else {
|
} else {
|
||||||
// For answering peer connection we set remote SDP and then
|
// For answering peer connection we set remote SDP and then
|
||||||
// create answer and set local SDP.
|
// create answer and set local SDP.
|
||||||
if (pc.getLocalDescription() != null) {
|
if (peerConnection.getLocalDescription() != null) {
|
||||||
// We've just set our local SDP so time to send it, drain
|
// We've just set our local SDP so time to send it, drain
|
||||||
// remote and send local ICE candidates.
|
// remote and send local ICE candidates.
|
||||||
Log.d(TAG, "Local SDP set succesfully");
|
Log.d(TAG, "Local SDP set succesfully");
|
||||||
|
@ -56,6 +56,8 @@ public class RoomParametersFetcher {
|
|||||||
private static final String TAG = "RoomRTCClient";
|
private static final String TAG = "RoomRTCClient";
|
||||||
private final RoomParametersFetcherEvents events;
|
private final RoomParametersFetcherEvents events;
|
||||||
private final boolean loopback;
|
private final boolean loopback;
|
||||||
|
private final String registerUrl;
|
||||||
|
private final String registerMessage;
|
||||||
private AsyncHttpURLConnection httpConnection;
|
private AsyncHttpURLConnection httpConnection;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -75,12 +77,17 @@ public class RoomParametersFetcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public RoomParametersFetcher(boolean loopback, String registerUrl,
|
public RoomParametersFetcher(boolean loopback, String registerUrl,
|
||||||
final RoomParametersFetcherEvents events) {
|
String registerMessage, final RoomParametersFetcherEvents events) {
|
||||||
Log.d(TAG, "Connecting to room: " + registerUrl);
|
|
||||||
this.loopback = loopback;
|
this.loopback = loopback;
|
||||||
|
this.registerUrl = registerUrl;
|
||||||
|
this.registerMessage = registerMessage;
|
||||||
this.events = events;
|
this.events = events;
|
||||||
|
}
|
||||||
|
|
||||||
httpConnection = new AsyncHttpURLConnection("POST", registerUrl, null,
|
public void makeRequest() {
|
||||||
|
Log.d(TAG, "Connecting to room: " + registerUrl);
|
||||||
|
httpConnection = new AsyncHttpURLConnection(
|
||||||
|
"POST", registerUrl, registerMessage,
|
||||||
new AsyncHttpEvents() {
|
new AsyncHttpEvents() {
|
||||||
@Override
|
@Override
|
||||||
public void onHttpError(String errorMessage) {
|
public void onHttpError(String errorMessage) {
|
||||||
@ -179,8 +186,7 @@ public class RoomParametersFetcher {
|
|||||||
SignalingParameters params = new SignalingParameters(
|
SignalingParameters params = new SignalingParameters(
|
||||||
iceServers, initiator,
|
iceServers, initiator,
|
||||||
pcConstraints, videoConstraints, audioConstraints,
|
pcConstraints, videoConstraints, audioConstraints,
|
||||||
roomId, clientId,
|
clientId, wssUrl, wssPostUrl,
|
||||||
wssUrl, wssPostUrl,
|
|
||||||
offerSdp, iceCandidates);
|
offerSdp, iceCandidates);
|
||||||
events.onSignalingParametersReady(params);
|
events.onSignalingParametersReady(params);
|
||||||
} catch (JSONException e) {
|
} catch (JSONException e) {
|
||||||
|
@ -47,6 +47,7 @@ public class SettingsActivity extends Activity
|
|||||||
private String keyprefHwCodec;
|
private String keyprefHwCodec;
|
||||||
private String keyprefCpuUsageDetection;
|
private String keyprefCpuUsageDetection;
|
||||||
private String keyPrefRoomServerUrl;
|
private String keyPrefRoomServerUrl;
|
||||||
|
private String keyPrefDisplayHud;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
@ -59,6 +60,7 @@ public class SettingsActivity extends Activity
|
|||||||
keyprefHwCodec = getString(R.string.pref_hwcodec_key);
|
keyprefHwCodec = getString(R.string.pref_hwcodec_key);
|
||||||
keyprefCpuUsageDetection = getString(R.string.pref_cpu_usage_detection_key);
|
keyprefCpuUsageDetection = getString(R.string.pref_cpu_usage_detection_key);
|
||||||
keyPrefRoomServerUrl = getString(R.string.pref_room_server_url_key);
|
keyPrefRoomServerUrl = getString(R.string.pref_room_server_url_key);
|
||||||
|
keyPrefDisplayHud = getString(R.string.pref_displayhud_key);
|
||||||
|
|
||||||
// Display the fragment as the main content.
|
// Display the fragment as the main content.
|
||||||
settingsFragment = new SettingsFragment();
|
settingsFragment = new SettingsFragment();
|
||||||
@ -83,6 +85,7 @@ public class SettingsActivity extends Activity
|
|||||||
updateSummaryB(sharedPreferences, keyprefHwCodec);
|
updateSummaryB(sharedPreferences, keyprefHwCodec);
|
||||||
updateSummaryB(sharedPreferences, keyprefCpuUsageDetection);
|
updateSummaryB(sharedPreferences, keyprefCpuUsageDetection);
|
||||||
updateSummary(sharedPreferences, keyPrefRoomServerUrl);
|
updateSummary(sharedPreferences, keyPrefRoomServerUrl);
|
||||||
|
updateSummaryB(sharedPreferences, keyPrefDisplayHud);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -105,7 +108,7 @@ public class SettingsActivity extends Activity
|
|||||||
} else if (key.equals(keyprefStartBitrateValue)) {
|
} else if (key.equals(keyprefStartBitrateValue)) {
|
||||||
updateSummaryBitrate(sharedPreferences, key);
|
updateSummaryBitrate(sharedPreferences, key);
|
||||||
} else if (key.equals(keyprefCpuUsageDetection)
|
} else if (key.equals(keyprefCpuUsageDetection)
|
||||||
|| key.equals(keyprefHwCodec)) {
|
|| key.equals(keyprefHwCodec) || key.equals(keyPrefDisplayHud)) {
|
||||||
updateSummaryB(sharedPreferences, key);
|
updateSummaryB(sharedPreferences, key);
|
||||||
}
|
}
|
||||||
if (key.equals(keyprefStartBitrateType)) {
|
if (key.equals(keyprefStartBitrateType)) {
|
||||||
|
@ -54,43 +54,43 @@ import org.webrtc.SessionDescription;
|
|||||||
public class WebSocketRTCClient implements AppRTCClient,
|
public class WebSocketRTCClient implements AppRTCClient,
|
||||||
WebSocketChannelEvents {
|
WebSocketChannelEvents {
|
||||||
private static final String TAG = "WSRTCClient";
|
private static final String TAG = "WSRTCClient";
|
||||||
public static final String GAE_JOIN = "join";
|
private static final String ROOM_JOIN = "join";
|
||||||
private static final String GAE_MESSAGE = "message";
|
private static final String ROOM_MESSAGE = "message";
|
||||||
private static final String GAE_LEAVE = "leave";
|
private static final String ROOM_LEAVE = "leave";
|
||||||
|
|
||||||
private enum ConnectionState {
|
private enum ConnectionState {
|
||||||
NEW, CONNECTED, CLOSED, ERROR
|
NEW, CONNECTED, CLOSED, ERROR
|
||||||
};
|
};
|
||||||
private enum MessageType {
|
private enum MessageType {
|
||||||
MESSAGE, BYE
|
MESSAGE, LEAVE
|
||||||
};
|
};
|
||||||
private final LooperExecutor executor;
|
private final LooperExecutor executor;
|
||||||
private boolean loopback;
|
|
||||||
private boolean initiator;
|
private boolean initiator;
|
||||||
private SignalingEvents events;
|
private SignalingEvents events;
|
||||||
private WebSocketChannelClient wsClient;
|
private WebSocketChannelClient wsClient;
|
||||||
private ConnectionState roomState;
|
private ConnectionState roomState;
|
||||||
private String roomUrl;
|
private RoomConnectionParameters connectionParameters;
|
||||||
private String postMessageUrl;
|
private String messageUrl;
|
||||||
private String byeMessageUrl;
|
private String leaveUrl;
|
||||||
|
|
||||||
public WebSocketRTCClient(SignalingEvents events) {
|
public WebSocketRTCClient(SignalingEvents events, LooperExecutor executor) {
|
||||||
this.events = events;
|
this.events = events;
|
||||||
executor = new LooperExecutor();
|
this.executor = executor;
|
||||||
|
roomState = ConnectionState.NEW;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --------------------------------------------------------------------
|
// --------------------------------------------------------------------
|
||||||
// AppRTCClient interface implementation.
|
// AppRTCClient interface implementation.
|
||||||
// Asynchronously connect to an AppRTC room URL, e.g.
|
// Asynchronously connect to an AppRTC room URL using supplied connection
|
||||||
// https://apprtc.appspot.com/register/<room>, retrieve room parameters
|
// parameters, retrieves room parameters and connect to WebSocket server.
|
||||||
// and connect to WebSocket server.
|
|
||||||
@Override
|
@Override
|
||||||
public void connectToRoom(final String url, final boolean loopback) {
|
public void connectToRoom(RoomConnectionParameters connectionParameters) {
|
||||||
|
this.connectionParameters = connectionParameters;
|
||||||
executor.requestStart();
|
executor.requestStart();
|
||||||
executor.execute(new Runnable() {
|
executor.execute(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
connectToRoomInternal(url, loopback);
|
connectToRoomInternal();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -107,33 +107,32 @@ public class WebSocketRTCClient implements AppRTCClient,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Connects to room - function runs on a local looper thread.
|
// Connects to room - function runs on a local looper thread.
|
||||||
private void connectToRoomInternal(String url, boolean loopback) {
|
private void connectToRoomInternal() {
|
||||||
Log.d(TAG, "Connect to room: " + url);
|
String connectionUrl = getConnectionUrl(connectionParameters);
|
||||||
this.loopback = loopback;
|
Log.d(TAG, "Connect to room: " + connectionUrl);
|
||||||
roomState = ConnectionState.NEW;
|
roomState = ConnectionState.NEW;
|
||||||
roomUrl = url.substring(0, url.indexOf("/" + GAE_JOIN));
|
|
||||||
// Create WebSocket client.
|
|
||||||
wsClient = new WebSocketChannelClient(executor, this);
|
wsClient = new WebSocketChannelClient(executor, this);
|
||||||
// Get room parameters.
|
|
||||||
new RoomParametersFetcher(loopback, url,
|
RoomParametersFetcherEvents callbacks = new RoomParametersFetcherEvents() {
|
||||||
new RoomParametersFetcherEvents() {
|
|
||||||
@Override
|
@Override
|
||||||
public void onSignalingParametersReady(
|
public void onSignalingParametersReady(
|
||||||
final SignalingParameters params) {
|
final SignalingParameters params) {
|
||||||
executor.execute(new Runnable() {
|
WebSocketRTCClient.this.executor.execute(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
signalingParametersReady(params);
|
WebSocketRTCClient.this.signalingParametersReady(params);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSignalingParametersError(String description) {
|
public void onSignalingParametersError(String description) {
|
||||||
reportError(description);
|
WebSocketRTCClient.this.reportError(description);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
);
|
|
||||||
|
new RoomParametersFetcher(connectionParameters.loopback, connectionUrl,
|
||||||
|
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.
|
||||||
@ -141,7 +140,7 @@ public class WebSocketRTCClient implements AppRTCClient,
|
|||||||
Log.d(TAG, "Disconnect. Room state: " + roomState);
|
Log.d(TAG, "Disconnect. Room state: " + roomState);
|
||||||
if (roomState == ConnectionState.CONNECTED) {
|
if (roomState == ConnectionState.CONNECTED) {
|
||||||
Log.d(TAG, "Closing room.");
|
Log.d(TAG, "Closing room.");
|
||||||
sendPostMessage(MessageType.BYE, byeMessageUrl, "");
|
sendPostMessage(MessageType.LEAVE, leaveUrl, null);
|
||||||
}
|
}
|
||||||
roomState = ConnectionState.CLOSED;
|
roomState = ConnectionState.CLOSED;
|
||||||
if (wsClient != null) {
|
if (wsClient != null) {
|
||||||
@ -150,57 +149,53 @@ public class WebSocketRTCClient implements AppRTCClient,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Helper functions to get connection, post message and leave message URLs
|
// Helper functions to get connection, post message and leave message URLs
|
||||||
public static String getGAEConnectionUrl(String roomUrl, String roomId) {
|
private String getConnectionUrl(
|
||||||
return roomUrl + "/" + GAE_JOIN + "/" + roomId;
|
RoomConnectionParameters connectionParameters) {
|
||||||
|
return connectionParameters.roomUrl + "/" + ROOM_JOIN + "/"
|
||||||
|
+ connectionParameters.roomId;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getGAEPostMessageUrl(String roomUrl, String roomId,
|
private String getMessageUrl(RoomConnectionParameters connectionParameters,
|
||||||
String clientId) {
|
SignalingParameters signalingParameters) {
|
||||||
return roomUrl + "/" + GAE_MESSAGE + "/" + roomId + "/" + clientId;
|
return connectionParameters.roomUrl + "/" + ROOM_MESSAGE + "/"
|
||||||
|
+ connectionParameters.roomId + "/" + signalingParameters.clientId;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getGAEByeMessageUrl(String roomUrl, String roomId,
|
private String getLeaveUrl(RoomConnectionParameters connectionParameters,
|
||||||
String clientId) {
|
SignalingParameters signalingParameters) {
|
||||||
return roomUrl + "/" + GAE_LEAVE + "/" + roomId + "/" + clientId;
|
return connectionParameters.roomUrl + "/" + ROOM_LEAVE + "/"
|
||||||
|
+ connectionParameters.roomId + "/" + signalingParameters.clientId;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Callback issued when room parameters are extracted. Runs on local
|
// Callback issued when room parameters are extracted. Runs on local
|
||||||
// looper thread.
|
// looper thread.
|
||||||
private void signalingParametersReady(final SignalingParameters params) {
|
private void signalingParametersReady(
|
||||||
|
final SignalingParameters signalingParameters) {
|
||||||
Log.d(TAG, "Room connection completed.");
|
Log.d(TAG, "Room connection completed.");
|
||||||
if (loopback && (!params.initiator || params.offerSdp != null)) {
|
if (connectionParameters.loopback
|
||||||
|
&& (!signalingParameters.initiator
|
||||||
|
|| signalingParameters.offerSdp != null)) {
|
||||||
reportError("Loopback room is busy.");
|
reportError("Loopback room is busy.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!loopback && !params.initiator && params.offerSdp == null) {
|
if (!connectionParameters.loopback
|
||||||
|
&& !signalingParameters.initiator
|
||||||
|
&& signalingParameters.offerSdp == null) {
|
||||||
Log.w(TAG, "No offer SDP in room response.");
|
Log.w(TAG, "No offer SDP in room response.");
|
||||||
}
|
}
|
||||||
initiator = params.initiator;
|
initiator = signalingParameters.initiator;
|
||||||
postMessageUrl = getGAEPostMessageUrl(
|
messageUrl = getMessageUrl(connectionParameters, signalingParameters);
|
||||||
roomUrl, params.roomId, params.clientId);
|
leaveUrl = getLeaveUrl(connectionParameters, signalingParameters);
|
||||||
byeMessageUrl = getGAEByeMessageUrl(
|
Log.d(TAG, "Message URL: " + messageUrl);
|
||||||
roomUrl, params.roomId, params.clientId);
|
Log.d(TAG, "Leave URL: " + leaveUrl);
|
||||||
roomState = ConnectionState.CONNECTED;
|
roomState = ConnectionState.CONNECTED;
|
||||||
|
|
||||||
// Fire connection and signaling parameters events.
|
// Fire connection and signaling parameters events.
|
||||||
events.onConnectedToRoom(params);
|
events.onConnectedToRoom(signalingParameters);
|
||||||
|
|
||||||
// Connect to WebSocket server.
|
// Connect to WebSocket server.
|
||||||
wsClient.connect(
|
wsClient.connect(signalingParameters.wssUrl, signalingParameters.wssPostUrl,
|
||||||
params.wssUrl, params.wssPostUrl, params.roomId, params.clientId);
|
connectionParameters.roomId, signalingParameters.clientId);
|
||||||
|
|
||||||
// For call receiver get sdp offer and ice candidates
|
|
||||||
// from room parameters and fire corresponding events.
|
|
||||||
if (!params.initiator) {
|
|
||||||
if (params.offerSdp != null) {
|
|
||||||
events.onRemoteDescription(params.offerSdp);
|
|
||||||
}
|
|
||||||
if (params.iceCandidates != null) {
|
|
||||||
for (IceCandidate iceCandidate : params.iceCandidates) {
|
|
||||||
events.onRemoteIceCandidate(iceCandidate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send local offer SDP to the other participant.
|
// Send local offer SDP to the other participant.
|
||||||
@ -216,8 +211,8 @@ public class WebSocketRTCClient implements AppRTCClient,
|
|||||||
JSONObject json = new JSONObject();
|
JSONObject json = new JSONObject();
|
||||||
jsonPut(json, "sdp", sdp.description);
|
jsonPut(json, "sdp", sdp.description);
|
||||||
jsonPut(json, "type", "offer");
|
jsonPut(json, "type", "offer");
|
||||||
sendPostMessage(MessageType.MESSAGE, postMessageUrl, json.toString());
|
sendPostMessage(MessageType.MESSAGE, messageUrl, json.toString());
|
||||||
if (loopback) {
|
if (connectionParameters.loopback) {
|
||||||
// In loopback mode rename this offer to answer and route it back.
|
// In loopback mode rename this offer to answer and route it back.
|
||||||
SessionDescription sdpAnswer = new SessionDescription(
|
SessionDescription sdpAnswer = new SessionDescription(
|
||||||
SessionDescription.Type.fromCanonicalForm("answer"),
|
SessionDescription.Type.fromCanonicalForm("answer"),
|
||||||
@ -234,7 +229,7 @@ public class WebSocketRTCClient implements AppRTCClient,
|
|||||||
executor.execute(new Runnable() {
|
executor.execute(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
if (loopback) {
|
if (connectionParameters.loopback) {
|
||||||
Log.e(TAG, "Sending answer in loopback mode.");
|
Log.e(TAG, "Sending answer in loopback mode.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -267,8 +262,8 @@ public class WebSocketRTCClient implements AppRTCClient,
|
|||||||
reportError("Sending ICE candidate in non connected state.");
|
reportError("Sending ICE candidate in non connected state.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
sendPostMessage(MessageType.MESSAGE, postMessageUrl, json.toString());
|
sendPostMessage(MessageType.MESSAGE, messageUrl, json.toString());
|
||||||
if (loopback) {
|
if (connectionParameters.loopback) {
|
||||||
events.onRemoteIceCandidate(candidate);
|
events.onRemoteIceCandidate(candidate);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -384,11 +379,11 @@ public class WebSocketRTCClient implements AppRTCClient,
|
|||||||
// Send SDP or ICE candidate to a room server.
|
// Send SDP or ICE candidate to a room server.
|
||||||
private void sendPostMessage(
|
private void sendPostMessage(
|
||||||
final MessageType messageType, final String url, final String message) {
|
final MessageType messageType, final String url, final String message) {
|
||||||
if (messageType == MessageType.BYE) {
|
String logInfo = url;
|
||||||
Log.d(TAG, "C->GAE: " + url);
|
if (message != null) {
|
||||||
} else {
|
logInfo += ". Message: " + message;
|
||||||
Log.d(TAG, "C->GAE: " + message);
|
|
||||||
}
|
}
|
||||||
|
Log.d(TAG, "C->GAE: " + logInfo);
|
||||||
AsyncHttpURLConnection httpConnection = new AsyncHttpURLConnection(
|
AsyncHttpURLConnection httpConnection = new AsyncHttpURLConnection(
|
||||||
"POST", url, message, new AsyncHttpEvents() {
|
"POST", url, message, new AsyncHttpEvents() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -36,7 +36,7 @@ import java.net.URL;
|
|||||||
import java.util.Scanner;
|
import java.util.Scanner;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Asynchronious http requests implementation.
|
* Asynchronous http requests implementation.
|
||||||
*/
|
*/
|
||||||
public class AsyncHttpURLConnection {
|
public class AsyncHttpURLConnection {
|
||||||
private static final int HTTP_TIMEOUT_MS = 5000;
|
private static final int HTTP_TIMEOUT_MS = 5000;
|
||||||
|
@ -35,11 +35,13 @@ import java.util.concurrent.TimeUnit;
|
|||||||
import org.appspot.apprtc.AppRTCClient.SignalingParameters;
|
import org.appspot.apprtc.AppRTCClient.SignalingParameters;
|
||||||
import org.appspot.apprtc.PeerConnectionClient;
|
import org.appspot.apprtc.PeerConnectionClient;
|
||||||
import org.appspot.apprtc.PeerConnectionClient.PeerConnectionEvents;
|
import org.appspot.apprtc.PeerConnectionClient.PeerConnectionEvents;
|
||||||
|
import org.appspot.apprtc.PeerConnectionClient.PeerConnectionParameters;
|
||||||
import org.appspot.apprtc.util.LooperExecutor;
|
import org.appspot.apprtc.util.LooperExecutor;
|
||||||
import org.webrtc.IceCandidate;
|
import org.webrtc.IceCandidate;
|
||||||
import org.webrtc.MediaConstraints;
|
import org.webrtc.MediaConstraints;
|
||||||
import org.webrtc.PeerConnection;
|
import org.webrtc.PeerConnection;
|
||||||
import org.webrtc.SessionDescription;
|
import org.webrtc.SessionDescription;
|
||||||
|
import org.webrtc.StatsReport;
|
||||||
import org.webrtc.VideoRenderer;
|
import org.webrtc.VideoRenderer;
|
||||||
|
|
||||||
import android.test.InstrumentationTestCase;
|
import android.test.InstrumentationTestCase;
|
||||||
@ -167,6 +169,10 @@ public class PeerConnectionClientTest extends InstrumentationTestCase
|
|||||||
fail("PC Error: " + description);
|
fail("PC Error: " + description);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPeerConnectionStatsReady(StatsReport[] reports) {
|
||||||
|
}
|
||||||
|
|
||||||
// Helper wait functions.
|
// Helper wait functions.
|
||||||
private boolean waitForLocalSDP(int timeoutMs)
|
private boolean waitForLocalSDP(int timeoutMs)
|
||||||
throws InterruptedException {
|
throws InterruptedException {
|
||||||
@ -220,8 +226,7 @@ public class PeerConnectionClientTest extends InstrumentationTestCase
|
|||||||
SignalingParameters signalingParameters = new SignalingParameters(
|
SignalingParameters signalingParameters = new SignalingParameters(
|
||||||
iceServers, true,
|
iceServers, true,
|
||||||
pcConstraints, videoConstraints, audioConstraints,
|
pcConstraints, videoConstraints, audioConstraints,
|
||||||
null, null,
|
null, null, null,
|
||||||
null, null,
|
|
||||||
null, null);
|
null, null);
|
||||||
return signalingParameters;
|
return signalingParameters;
|
||||||
}
|
}
|
||||||
@ -229,12 +234,14 @@ public class PeerConnectionClientTest extends InstrumentationTestCase
|
|||||||
PeerConnectionClient createPeerConnectionClient(MockRenderer localRenderer,
|
PeerConnectionClient createPeerConnectionClient(MockRenderer localRenderer,
|
||||||
MockRenderer remoteRenderer) {
|
MockRenderer remoteRenderer) {
|
||||||
SignalingParameters signalingParameters = getTestSignalingParameters();
|
SignalingParameters signalingParameters = getTestSignalingParameters();
|
||||||
|
PeerConnectionParameters peerConnectionParameters =
|
||||||
|
new PeerConnectionParameters(0, 0, 0, 0, false);
|
||||||
|
|
||||||
PeerConnectionClient client = new PeerConnectionClient();
|
PeerConnectionClient client = new PeerConnectionClient();
|
||||||
client.createPeerConnectionFactory(
|
client.createPeerConnectionFactory(
|
||||||
getInstrumentation().getContext(), "VP8", true, null, this);
|
getInstrumentation().getContext(), "VP8", true, null, this);
|
||||||
client.createPeerConnection(
|
client.createPeerConnection(localRenderer, remoteRenderer,
|
||||||
localRenderer, remoteRenderer, signalingParameters, 1000);
|
signalingParameters, peerConnectionParameters);
|
||||||
client.createOffer();
|
client.createOffer();
|
||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
@ -313,4 +320,5 @@ public class PeerConnectionClientTest extends InstrumentationTestCase
|
|||||||
assertTrue(waitForPeerConnectionClosed(WAIT_TIMEOUT));
|
assertTrue(waitForPeerConnectionClosed(WAIT_TIMEOUT));
|
||||||
Log.d(TAG, "testLoopback Done.");
|
Log.d(TAG, "testLoopback Done.");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -355,17 +355,18 @@
|
|||||||
'examples/android/res/drawable-xhdpi/ic_action_return_from_full_screen.png',
|
'examples/android/res/drawable-xhdpi/ic_action_return_from_full_screen.png',
|
||||||
'examples/android/res/drawable-xhdpi/ic_loopback_call.png',
|
'examples/android/res/drawable-xhdpi/ic_loopback_call.png',
|
||||||
'examples/android/res/drawable-xhdpi/ic_launcher.png',
|
'examples/android/res/drawable-xhdpi/ic_launcher.png',
|
||||||
|
'examples/android/res/layout/activity_call.xml',
|
||||||
'examples/android/res/layout/activity_connect.xml',
|
'examples/android/res/layout/activity_connect.xml',
|
||||||
'examples/android/res/layout/activity_fullscreen.xml',
|
'examples/android/res/layout/fragment_call.xml',
|
||||||
'examples/android/res/layout/fragment_menubar.xml',
|
|
||||||
'examples/android/res/menu/connect_menu.xml',
|
'examples/android/res/menu/connect_menu.xml',
|
||||||
'examples/android/res/values/arrays.xml',
|
'examples/android/res/values/arrays.xml',
|
||||||
'examples/android/res/values/strings.xml',
|
'examples/android/res/values/strings.xml',
|
||||||
'examples/android/res/xml/preferences.xml',
|
'examples/android/res/xml/preferences.xml',
|
||||||
'examples/android/src/org/appspot/apprtc/AppRTCAudioManager.java',
|
'examples/android/src/org/appspot/apprtc/AppRTCAudioManager.java',
|
||||||
'examples/android/src/org/appspot/apprtc/AppRTCClient.java',
|
'examples/android/src/org/appspot/apprtc/AppRTCClient.java',
|
||||||
'examples/android/src/org/appspot/apprtc/AppRTCDemoActivity.java',
|
|
||||||
'examples/android/src/org/appspot/apprtc/AppRTCProximitySensor.java',
|
'examples/android/src/org/appspot/apprtc/AppRTCProximitySensor.java',
|
||||||
|
'examples/android/src/org/appspot/apprtc/CallActivity.java',
|
||||||
|
'examples/android/src/org/appspot/apprtc/CallFragment.java',
|
||||||
'examples/android/src/org/appspot/apprtc/ConnectActivity.java',
|
'examples/android/src/org/appspot/apprtc/ConnectActivity.java',
|
||||||
'examples/android/src/org/appspot/apprtc/PeerConnectionClient.java',
|
'examples/android/src/org/appspot/apprtc/PeerConnectionClient.java',
|
||||||
'examples/android/src/org/appspot/apprtc/RoomParametersFetcher.java',
|
'examples/android/src/org/appspot/apprtc/RoomParametersFetcher.java',
|
||||||
|
Reference in New Issue
Block a user