Port some fixes in AppRTCDemo.
- Make PeerConnectionClient a singleton. - Fix crash in CpuMonitor. - Remove reading constraints from room response. - Catch and report camera errors. R=wzh@webrtc.org Review URL: https://webrtc-codereview.appspot.com/43059004 Cr-Commit-Position: refs/heads/master@{#8930}
This commit is contained in:
parent
be508a1d36
commit
e095148869
@ -28,7 +28,6 @@
|
|||||||
package org.appspot.apprtc;
|
package org.appspot.apprtc;
|
||||||
|
|
||||||
import org.webrtc.IceCandidate;
|
import org.webrtc.IceCandidate;
|
||||||
import org.webrtc.MediaConstraints;
|
|
||||||
import org.webrtc.PeerConnection;
|
import org.webrtc.PeerConnection;
|
||||||
import org.webrtc.SessionDescription;
|
import org.webrtc.SessionDescription;
|
||||||
|
|
||||||
@ -87,9 +86,6 @@ public interface AppRTCClient {
|
|||||||
public static 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 videoConstraints;
|
|
||||||
public final MediaConstraints audioConstraints;
|
|
||||||
public final String clientId;
|
public final String clientId;
|
||||||
public final String wssUrl;
|
public final String wssUrl;
|
||||||
public final String wssPostUrl;
|
public final String wssPostUrl;
|
||||||
@ -98,15 +94,11 @@ public interface AppRTCClient {
|
|||||||
|
|
||||||
public SignalingParameters(
|
public SignalingParameters(
|
||||||
List<PeerConnection.IceServer> iceServers,
|
List<PeerConnection.IceServer> iceServers,
|
||||||
boolean initiator, MediaConstraints pcConstraints,
|
boolean initiator, String clientId,
|
||||||
MediaConstraints videoConstraints, MediaConstraints audioConstraints,
|
String wssUrl, String wssPostUrl,
|
||||||
String clientId, 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.videoConstraints = videoConstraints;
|
|
||||||
this.audioConstraints = audioConstraints;
|
|
||||||
this.clientId = clientId;
|
this.clientId = clientId;
|
||||||
this.wssUrl = wssUrl;
|
this.wssUrl = wssUrl;
|
||||||
this.wssPostUrl = wssPostUrl;
|
this.wssPostUrl = wssPostUrl;
|
||||||
|
@ -383,7 +383,7 @@ public class CallActivity extends Activity
|
|||||||
if (peerConnectionClient == null) {
|
if (peerConnectionClient == null) {
|
||||||
final long delta = System.currentTimeMillis() - callStartedTimeMs;
|
final long delta = System.currentTimeMillis() - callStartedTimeMs;
|
||||||
Log.d(TAG, "Creating peer connection factory, delay=" + delta + "ms");
|
Log.d(TAG, "Creating peer connection factory, delay=" + delta + "ms");
|
||||||
peerConnectionClient = new PeerConnectionClient();
|
peerConnectionClient = PeerConnectionClient.getInstance();
|
||||||
peerConnectionClient.createPeerConnectionFactory(CallActivity.this,
|
peerConnectionClient.createPeerConnectionFactory(CallActivity.this,
|
||||||
VideoRendererGui.getEGLContext(), peerConnectionParameters,
|
VideoRendererGui.getEGLContext(), peerConnectionParameters,
|
||||||
CallActivity.this);
|
CallActivity.this);
|
||||||
|
@ -113,7 +113,8 @@ class CpuMonitor {
|
|||||||
Scanner scanner = new Scanner(rdr).useDelimiter("[-\n]");
|
Scanner scanner = new Scanner(rdr).useDelimiter("[-\n]");
|
||||||
scanner.nextInt(); // Skip leading number 0.
|
scanner.nextInt(); // Skip leading number 0.
|
||||||
cpusPresent = 1 + scanner.nextInt();
|
cpusPresent = 1 + scanner.nextInt();
|
||||||
} catch (InputMismatchException e) {
|
scanner.close();
|
||||||
|
} catch (Exception e) {
|
||||||
Log.e(TAG, "Cannot do CPU stats due to /sys/devices/system/cpu/present parsing problem");
|
Log.e(TAG, "Cannot do CPU stats due to /sys/devices/system/cpu/present parsing problem");
|
||||||
} finally {
|
} finally {
|
||||||
fin.close();
|
fin.close();
|
||||||
@ -264,7 +265,8 @@ class CpuMonitor {
|
|||||||
BufferedReader rdr = new BufferedReader(fin);
|
BufferedReader rdr = new BufferedReader(fin);
|
||||||
Scanner scannerC = new Scanner(rdr);
|
Scanner scannerC = new Scanner(rdr);
|
||||||
number = scannerC.nextLong();
|
number = scannerC.nextLong();
|
||||||
} catch (InputMismatchException e) {
|
scannerC.close();
|
||||||
|
} catch (Exception e) {
|
||||||
// CPU presumably got offline just after we opened file.
|
// CPU presumably got offline just after we opened file.
|
||||||
} finally {
|
} finally {
|
||||||
fin.close();
|
fin.close();
|
||||||
@ -295,7 +297,8 @@ class CpuMonitor {
|
|||||||
long sys = scanner.nextLong();
|
long sys = scanner.nextLong();
|
||||||
runTime = user + nice + sys;
|
runTime = user + nice + sys;
|
||||||
idleTime = scanner.nextLong();
|
idleTime = scanner.nextLong();
|
||||||
} catch (InputMismatchException e) {
|
scanner.close();
|
||||||
|
} catch (Exception e) {
|
||||||
Log.e(TAG, "Problems parsing /proc/stat");
|
Log.e(TAG, "Problems parsing /proc/stat");
|
||||||
return null;
|
return null;
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -35,6 +35,7 @@ import org.appspot.apprtc.AppRTCClient.SignalingParameters;
|
|||||||
import org.appspot.apprtc.util.LooperExecutor;
|
import org.appspot.apprtc.util.LooperExecutor;
|
||||||
import org.webrtc.DataChannel;
|
import org.webrtc.DataChannel;
|
||||||
import org.webrtc.IceCandidate;
|
import org.webrtc.IceCandidate;
|
||||||
|
import org.webrtc.Logging;
|
||||||
import org.webrtc.MediaCodecVideoEncoder;
|
import org.webrtc.MediaCodecVideoEncoder;
|
||||||
import org.webrtc.MediaConstraints;
|
import org.webrtc.MediaConstraints;
|
||||||
import org.webrtc.MediaConstraints.KeyValuePair;
|
import org.webrtc.MediaConstraints.KeyValuePair;
|
||||||
@ -51,6 +52,7 @@ import org.webrtc.VideoRenderer;
|
|||||||
import org.webrtc.VideoSource;
|
import org.webrtc.VideoSource;
|
||||||
import org.webrtc.VideoTrack;
|
import org.webrtc.VideoTrack;
|
||||||
|
|
||||||
|
import java.util.EnumSet;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.Timer;
|
import java.util.Timer;
|
||||||
import java.util.TimerTask;
|
import java.util.TimerTask;
|
||||||
@ -62,6 +64,7 @@ import java.util.regex.Pattern;
|
|||||||
*
|
*
|
||||||
* <p>All public methods are routed to local looper thread.
|
* <p>All public methods are routed to local looper thread.
|
||||||
* All PeerConnectionEvents callbacks are invoked from the same looper thread.
|
* All PeerConnectionEvents callbacks are invoked from the same looper thread.
|
||||||
|
* This class is a singleton.
|
||||||
*/
|
*/
|
||||||
public class PeerConnectionClient {
|
public class PeerConnectionClient {
|
||||||
public static final String VIDEO_TRACK_ID = "ARDAMSv0";
|
public static final String VIDEO_TRACK_ID = "ARDAMSv0";
|
||||||
@ -89,18 +92,21 @@ public class PeerConnectionClient {
|
|||||||
private static final int MAX_VIDEO_HEIGHT = 1280;
|
private static final int MAX_VIDEO_HEIGHT = 1280;
|
||||||
private static final int MAX_VIDEO_FPS = 30;
|
private static final int MAX_VIDEO_FPS = 30;
|
||||||
|
|
||||||
private final LooperExecutor executor;
|
private static final PeerConnectionClient instance = new PeerConnectionClient();
|
||||||
private PeerConnectionFactory factory = null;
|
|
||||||
private PeerConnection peerConnection = null;
|
|
||||||
private VideoSource videoSource;
|
|
||||||
private boolean videoCallEnabled = true;
|
|
||||||
private boolean preferIsac = false;
|
|
||||||
private boolean preferH264 = false;
|
|
||||||
private boolean videoSourceStopped = false;
|
|
||||||
private boolean isError = false;
|
|
||||||
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 final LooperExecutor executor;
|
||||||
|
|
||||||
|
private PeerConnectionFactory factory;
|
||||||
|
private PeerConnection peerConnection;
|
||||||
|
PeerConnectionFactory.Options options = null;
|
||||||
|
private VideoSource videoSource;
|
||||||
|
private boolean videoCallEnabled;
|
||||||
|
private boolean preferIsac;
|
||||||
|
private boolean preferH264;
|
||||||
|
private boolean videoSourceStopped;
|
||||||
|
private boolean isError;
|
||||||
|
private Timer statsTimer;
|
||||||
private VideoRenderer.Callbacks localRender;
|
private VideoRenderer.Callbacks localRender;
|
||||||
private VideoRenderer.Callbacks remoteRender;
|
private VideoRenderer.Callbacks remoteRender;
|
||||||
private SignalingParameters signalingParameters;
|
private SignalingParameters signalingParameters;
|
||||||
@ -112,17 +118,17 @@ public class PeerConnectionClient {
|
|||||||
// 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;
|
||||||
private PeerConnectionEvents events;
|
private PeerConnectionEvents events;
|
||||||
private boolean isInitiator;
|
private boolean isInitiator;
|
||||||
private SessionDescription localSdp = null; // either offer or answer SDP
|
private SessionDescription localSdp; // either offer or answer SDP
|
||||||
private MediaStream mediaStream = null;
|
private MediaStream mediaStream;
|
||||||
private int numberOfCameras;
|
private int numberOfCameras;
|
||||||
private VideoCapturerAndroid videoCapturer = null;
|
private VideoCapturerAndroid videoCapturer;
|
||||||
// enableVideo is set to true if video should be rendered and sent.
|
// enableVideo is set to true if video should be rendered and sent.
|
||||||
private boolean renderVideo = true;
|
private boolean renderVideo;
|
||||||
private VideoTrack localVideoTrack = null;
|
private VideoTrack localVideoTrack;
|
||||||
private VideoTrack remoteVideoTrack = null;
|
private VideoTrack remoteVideoTrack;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Peer connection parameters.
|
* Peer connection parameters.
|
||||||
@ -202,8 +208,20 @@ public class PeerConnectionClient {
|
|||||||
public void onPeerConnectionError(final String description);
|
public void onPeerConnectionError(final String description);
|
||||||
}
|
}
|
||||||
|
|
||||||
public PeerConnectionClient() {
|
private PeerConnectionClient() {
|
||||||
executor = new LooperExecutor();
|
executor = new LooperExecutor();
|
||||||
|
// Looper thread is started once in private ctor and is used for all
|
||||||
|
// peer connection API calls to ensure new peer connection factory is
|
||||||
|
// created on the same thread as previously destroyed factory.
|
||||||
|
executor.requestStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PeerConnectionClient getInstance() {
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPeerConnectionFactoryOptions(PeerConnectionFactory.Options options) {
|
||||||
|
this.options = options;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void createPeerConnectionFactory(
|
public void createPeerConnectionFactory(
|
||||||
@ -214,7 +232,22 @@ public class PeerConnectionClient {
|
|||||||
this.peerConnectionParameters = peerConnectionParameters;
|
this.peerConnectionParameters = peerConnectionParameters;
|
||||||
this.events = events;
|
this.events = events;
|
||||||
videoCallEnabled = peerConnectionParameters.videoCallEnabled;
|
videoCallEnabled = peerConnectionParameters.videoCallEnabled;
|
||||||
executor.requestStart();
|
// Reset variables to initial states.
|
||||||
|
factory = null;
|
||||||
|
peerConnection = null;
|
||||||
|
preferIsac = false;
|
||||||
|
preferH264 = false;
|
||||||
|
videoSourceStopped = false;
|
||||||
|
isError = false;
|
||||||
|
queuedRemoteCandidates = null;
|
||||||
|
localSdp = null; // either offer or answer SDP
|
||||||
|
mediaStream = null;
|
||||||
|
videoCapturer = null;
|
||||||
|
renderVideo = true;
|
||||||
|
localVideoTrack = null;
|
||||||
|
remoteVideoTrack = null;
|
||||||
|
statsTimer = new Timer();
|
||||||
|
|
||||||
executor.execute(new Runnable() {
|
executor.execute(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
@ -250,7 +283,10 @@ public class PeerConnectionClient {
|
|||||||
closeInternal();
|
closeInternal();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
executor.requestStop();
|
}
|
||||||
|
|
||||||
|
public boolean isVideoCallEnabled() {
|
||||||
|
return videoCallEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createPeerConnectionFactoryInternal(
|
private void createPeerConnectionFactoryInternal(
|
||||||
@ -284,16 +320,13 @@ public class PeerConnectionClient {
|
|||||||
events.onPeerConnectionError("Failed to initializeAndroidGlobals");
|
events.onPeerConnectionError("Failed to initializeAndroidGlobals");
|
||||||
}
|
}
|
||||||
factory = new PeerConnectionFactory();
|
factory = new PeerConnectionFactory();
|
||||||
configureFactory(factory);
|
if (options != null) {
|
||||||
|
Log.d(TAG, "Factory networkIgnoreMask option: " + options.networkIgnoreMask);
|
||||||
|
factory.setOptions(options);
|
||||||
|
}
|
||||||
Log.d(TAG, "Peer connection factory created.");
|
Log.d(TAG, "Peer connection factory created.");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Hook where tests can provide additional configuration for the factory.
|
|
||||||
*/
|
|
||||||
protected void configureFactory(PeerConnectionFactory factory) {
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createMediaConstraintsInternal() {
|
private void createMediaConstraintsInternal() {
|
||||||
// Create peer connection constraints.
|
// Create peer connection constraints.
|
||||||
pcConstraints = new MediaConstraints();
|
pcConstraints = new MediaConstraints();
|
||||||
@ -384,12 +417,12 @@ public class PeerConnectionClient {
|
|||||||
signalingParameters.iceServers, pcConstraints, pcObserver);
|
signalingParameters.iceServers, pcConstraints, pcObserver);
|
||||||
isInitiator = false;
|
isInitiator = false;
|
||||||
|
|
||||||
// Uncomment to get ALL WebRTC tracing and SENSITIVE libjingle logging.
|
// Set default WebRTC tracing and INFO libjingle logging.
|
||||||
// NOTE: this _must_ happen while |factory| is alive!
|
// NOTE: this _must_ happen while |factory| is alive!
|
||||||
// Logging.enableTracing(
|
Logging.enableTracing(
|
||||||
// "logcat:",
|
"logcat:",
|
||||||
// EnumSet.of(Logging.TraceLevel.TRACE_ALL),
|
EnumSet.of(Logging.TraceLevel.TRACE_DEFAULT),
|
||||||
// Logging.Severity.LS_SENSITIVE);
|
Logging.Severity.LS_INFO);
|
||||||
|
|
||||||
mediaStream = factory.createLocalMediaStream("ARDAMS");
|
mediaStream = factory.createLocalMediaStream("ARDAMS");
|
||||||
if (videoCallEnabled) {
|
if (videoCallEnabled) {
|
||||||
@ -401,6 +434,10 @@ public class PeerConnectionClient {
|
|||||||
}
|
}
|
||||||
Log.d(TAG, "Opening camera: " + cameraDeviceName);
|
Log.d(TAG, "Opening camera: " + cameraDeviceName);
|
||||||
videoCapturer = VideoCapturerAndroid.create(cameraDeviceName);
|
videoCapturer = VideoCapturerAndroid.create(cameraDeviceName);
|
||||||
|
if (videoCapturer == null) {
|
||||||
|
reportError("Failed to open camera");
|
||||||
|
return;
|
||||||
|
}
|
||||||
mediaStream.addTrack(createVideoTrack(videoCapturer));
|
mediaStream.addTrack(createVideoTrack(videoCapturer));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -419,6 +456,7 @@ public class PeerConnectionClient {
|
|||||||
peerConnection.dispose();
|
peerConnection.dispose();
|
||||||
peerConnection = null;
|
peerConnection = null;
|
||||||
}
|
}
|
||||||
|
Log.d(TAG, "Closing video source.");
|
||||||
if (videoSource != null) {
|
if (videoSource != null) {
|
||||||
videoSource.dispose();
|
videoSource.dispose();
|
||||||
videoSource = null;
|
videoSource = null;
|
||||||
@ -428,6 +466,7 @@ public class PeerConnectionClient {
|
|||||||
factory.dispose();
|
factory.dispose();
|
||||||
factory = null;
|
factory = null;
|
||||||
}
|
}
|
||||||
|
options = null;
|
||||||
Log.d(TAG, "Closing peer connection done.");
|
Log.d(TAG, "Closing peer connection done.");
|
||||||
events.onPeerConnectionClosed();
|
events.onPeerConnectionClosed();
|
||||||
}
|
}
|
||||||
@ -477,17 +516,21 @@ public class PeerConnectionClient {
|
|||||||
|
|
||||||
public void enableStatsEvents(boolean enable, int periodMs) {
|
public void enableStatsEvents(boolean enable, int periodMs) {
|
||||||
if (enable) {
|
if (enable) {
|
||||||
statsTimer.schedule(new TimerTask() {
|
try {
|
||||||
@Override
|
statsTimer.schedule(new TimerTask() {
|
||||||
public void run() {
|
@Override
|
||||||
executor.execute(new Runnable() {
|
public void run() {
|
||||||
@Override
|
executor.execute(new Runnable() {
|
||||||
public void run() {
|
@Override
|
||||||
getStats();
|
public void run() {
|
||||||
}
|
getStats();
|
||||||
});
|
}
|
||||||
}
|
});
|
||||||
}, 0, periodMs);
|
}
|
||||||
|
}, 0, periodMs);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Can not schedule statistics timer", e);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
statsTimer.cancel();
|
statsTimer.cancel();
|
||||||
}
|
}
|
||||||
@ -769,8 +812,10 @@ public class PeerConnectionClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void switchCameraInternal() {
|
private void switchCameraInternal() {
|
||||||
if (!videoCallEnabled || numberOfCameras < 2) {
|
if (!videoCallEnabled || numberOfCameras < 2 || isError || videoCapturer == null) {
|
||||||
return; // No video is sent or only one camera is available.
|
Log.e(TAG, "Failed to switch camera. Video: " + videoCallEnabled + ". Error : "
|
||||||
|
+ isError + ". Number of cameras: " + numberOfCameras);
|
||||||
|
return; // No video is sent or only one camera is available or error happened.
|
||||||
}
|
}
|
||||||
Log.d(TAG, "Switch camera");
|
Log.d(TAG, "Switch camera");
|
||||||
videoCapturer.switchCamera();
|
videoCapturer.switchCamera();
|
||||||
@ -780,9 +825,7 @@ public class PeerConnectionClient {
|
|||||||
executor.execute(new Runnable() {
|
executor.execute(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
if (peerConnection != null && !isError) {
|
switchCameraInternal();
|
||||||
switchCameraInternal();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,6 @@ import org.json.JSONArray;
|
|||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
import org.webrtc.IceCandidate;
|
import org.webrtc.IceCandidate;
|
||||||
import org.webrtc.MediaConstraints;
|
|
||||||
import org.webrtc.PeerConnection;
|
import org.webrtc.PeerConnection;
|
||||||
import org.webrtc.SessionDescription;
|
import org.webrtc.SessionDescription;
|
||||||
|
|
||||||
@ -170,18 +169,8 @@ public class RoomParametersFetcher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MediaConstraints pcConstraints = constraintsFromJSON(roomJson.getString("pc_constraints"));
|
|
||||||
Log.d(TAG, "pcConstraints: " + pcConstraints);
|
|
||||||
MediaConstraints videoConstraints = constraintsFromJSON(
|
|
||||||
getAVConstraints("video", roomJson.getString("media_constraints")));
|
|
||||||
Log.d(TAG, "videoConstraints: " + videoConstraints);
|
|
||||||
MediaConstraints audioConstraints = constraintsFromJSON(
|
|
||||||
getAVConstraints("audio", roomJson.getString("media_constraints")));
|
|
||||||
Log.d(TAG, "audioConstraints: " + audioConstraints);
|
|
||||||
|
|
||||||
SignalingParameters params = new SignalingParameters(
|
SignalingParameters params = new SignalingParameters(
|
||||||
iceServers, initiator,
|
iceServers, initiator,
|
||||||
pcConstraints, videoConstraints, audioConstraints,
|
|
||||||
clientId, wssUrl, wssPostUrl,
|
clientId, wssUrl, wssPostUrl,
|
||||||
offerSdp, iceCandidates);
|
offerSdp, iceCandidates);
|
||||||
events.onSignalingParametersReady(params);
|
events.onSignalingParametersReady(params);
|
||||||
@ -193,59 +182,6 @@ public class RoomParametersFetcher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the constraints specified for |type| of "audio" or "video" in
|
|
||||||
// |mediaConstraintsString|.
|
|
||||||
private String getAVConstraints (
|
|
||||||
String type, String mediaConstraintsString) throws JSONException {
|
|
||||||
JSONObject json = new JSONObject(mediaConstraintsString);
|
|
||||||
// Tricky handling of values that are allowed to be (boolean or
|
|
||||||
// MediaTrackConstraints) by the getUserMedia() spec. There are three
|
|
||||||
// cases below.
|
|
||||||
if (!json.has(type) || !json.optBoolean(type, true)) {
|
|
||||||
// Case 1: "audio"/"video" is not present, or is an explicit "false"
|
|
||||||
// boolean.
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (json.optBoolean(type, false)) {
|
|
||||||
// Case 2: "audio"/"video" is an explicit "true" boolean.
|
|
||||||
return "{\"mandatory\": {}, \"optional\": []}";
|
|
||||||
}
|
|
||||||
// Case 3: "audio"/"video" is an object.
|
|
||||||
return json.getJSONObject(type).toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private MediaConstraints constraintsFromJSON(String jsonString)
|
|
||||||
throws JSONException {
|
|
||||||
if (jsonString == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
MediaConstraints constraints = new MediaConstraints();
|
|
||||||
JSONObject json = new JSONObject(jsonString);
|
|
||||||
JSONObject mandatoryJSON = json.optJSONObject("mandatory");
|
|
||||||
if (mandatoryJSON != null) {
|
|
||||||
JSONArray mandatoryKeys = mandatoryJSON.names();
|
|
||||||
if (mandatoryKeys != null) {
|
|
||||||
for (int i = 0; i < mandatoryKeys.length(); ++i) {
|
|
||||||
String key = mandatoryKeys.getString(i);
|
|
||||||
String value = mandatoryJSON.getString(key);
|
|
||||||
constraints.mandatory.add(
|
|
||||||
new MediaConstraints.KeyValuePair(key, value));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
JSONArray optionalJSON = json.optJSONArray("optional");
|
|
||||||
if (optionalJSON != null) {
|
|
||||||
for (int i = 0; i < optionalJSON.length(); ++i) {
|
|
||||||
JSONObject keyValueDict = optionalJSON.getJSONObject(i);
|
|
||||||
String key = keyValueDict.names().getString(0);
|
|
||||||
String value = keyValueDict.getString(key);
|
|
||||||
constraints.optional.add(
|
|
||||||
new MediaConstraints.KeyValuePair(key, value));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return constraints;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Requests & returns a TURN ICE Server based on a request URL. Must be run
|
// Requests & returns a TURN ICE Server based on a request URL. Must be run
|
||||||
// off the main thread!
|
// off the main thread!
|
||||||
private LinkedList<PeerConnection.IceServer> requestTurnServers(String url)
|
private LinkedList<PeerConnection.IceServer> requestTurnServers(String url)
|
||||||
|
@ -127,7 +127,7 @@ public class WebSocketChannelClient {
|
|||||||
this.roomID = roomID;
|
this.roomID = roomID;
|
||||||
this.clientID = clientID;
|
this.clientID = clientID;
|
||||||
if (state != WebSocketConnectionState.CONNECTED) {
|
if (state != WebSocketConnectionState.CONNECTED) {
|
||||||
Log.d(TAG, "WebSocket register() in state " + state);
|
Log.w(TAG, "WebSocket register() in state " + state);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Log.d(TAG, "Registering WebSocket for room " + roomID + ". CLientID: " + clientID);
|
Log.d(TAG, "Registering WebSocket for room " + roomID + ". CLientID: " + clientID);
|
||||||
@ -190,17 +190,16 @@ public class WebSocketChannelClient {
|
|||||||
checkIfCalledOnValidThread();
|
checkIfCalledOnValidThread();
|
||||||
Log.d(TAG, "Disonnect WebSocket. State: " + state);
|
Log.d(TAG, "Disonnect WebSocket. State: " + state);
|
||||||
if (state == WebSocketConnectionState.REGISTERED) {
|
if (state == WebSocketConnectionState.REGISTERED) {
|
||||||
|
// Send "bye" to WebSocket server.
|
||||||
send("{\"type\": \"bye\"}");
|
send("{\"type\": \"bye\"}");
|
||||||
state = WebSocketConnectionState.CONNECTED;
|
state = WebSocketConnectionState.CONNECTED;
|
||||||
|
// Send http DELETE to http WebSocket server.
|
||||||
|
sendWSSMessage("DELETE", "");
|
||||||
}
|
}
|
||||||
// Close WebSocket in CONNECTED or ERROR states only.
|
// Close WebSocket in CONNECTED or ERROR states only.
|
||||||
if (state == WebSocketConnectionState.CONNECTED
|
if (state == WebSocketConnectionState.CONNECTED
|
||||||
|| state == WebSocketConnectionState.ERROR) {
|
|| state == WebSocketConnectionState.ERROR) {
|
||||||
ws.disconnect();
|
ws.disconnect();
|
||||||
|
|
||||||
// Send DELETE to http WebSocket server.
|
|
||||||
sendWSSMessage("DELETE", "");
|
|
||||||
|
|
||||||
state = WebSocketConnectionState.CLOSED;
|
state = WebSocketConnectionState.CLOSED;
|
||||||
|
|
||||||
// Wait for websocket close event to prevent websocket library from
|
// Wait for websocket close event to prevent websocket library from
|
||||||
|
@ -45,6 +45,7 @@ public class AsyncHttpURLConnection {
|
|||||||
private final String url;
|
private final String url;
|
||||||
private final String message;
|
private final String message;
|
||||||
private final AsyncHttpEvents events;
|
private final AsyncHttpEvents events;
|
||||||
|
private String contentType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Http requests callbacks.
|
* Http requests callbacks.
|
||||||
@ -62,6 +63,10 @@ public class AsyncHttpURLConnection {
|
|||||||
this.events = events;
|
this.events = events;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setContentType(String contentType) {
|
||||||
|
this.contentType = contentType;
|
||||||
|
}
|
||||||
|
|
||||||
public void send() {
|
public void send() {
|
||||||
Runnable runHttp = new Runnable() {
|
Runnable runHttp = new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
@ -92,8 +97,11 @@ public class AsyncHttpURLConnection {
|
|||||||
connection.setDoOutput(true);
|
connection.setDoOutput(true);
|
||||||
connection.setFixedLengthStreamingMode(postData.length);
|
connection.setFixedLengthStreamingMode(postData.length);
|
||||||
}
|
}
|
||||||
connection.setRequestProperty(
|
if (contentType == null) {
|
||||||
"content-type", "text/plain; charset=utf-8");
|
connection.setRequestProperty("Content-Type", "text/plain; charset=utf-8");
|
||||||
|
} else {
|
||||||
|
connection.setRequestProperty("Content-Type", contentType);
|
||||||
|
}
|
||||||
|
|
||||||
// Send POST request.
|
// Send POST request.
|
||||||
if (doOutput && postData.length > 0) {
|
if (doOutput && postData.length > 0) {
|
||||||
@ -105,9 +113,9 @@ public class AsyncHttpURLConnection {
|
|||||||
// Get response.
|
// Get response.
|
||||||
int responseCode = connection.getResponseCode();
|
int responseCode = connection.getResponseCode();
|
||||||
if (responseCode != 200) {
|
if (responseCode != 200) {
|
||||||
connection.disconnect();
|
|
||||||
events.onHttpError("Non-200 response to " + method + " to URL: "
|
events.onHttpError("Non-200 response to " + method + " to URL: "
|
||||||
+ url + " : " + connection.getHeaderField(null));
|
+ url + " : " + connection.getHeaderField(null));
|
||||||
|
connection.disconnect();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
InputStream responseStream = connection.getInputStream();
|
InputStream responseStream = connection.getInputStream();
|
||||||
|
@ -61,7 +61,6 @@ public class PeerConnectionClientTest extends InstrumentationTestCase
|
|||||||
private static final String VIDEO_CODEC_VP9 = "VP9";
|
private static final String VIDEO_CODEC_VP9 = "VP9";
|
||||||
private static final String VIDEO_CODEC_H264 = "H264";
|
private static final String VIDEO_CODEC_H264 = "H264";
|
||||||
private static final int AUDIO_RUN_TIMEOUT = 1000;
|
private static final int AUDIO_RUN_TIMEOUT = 1000;
|
||||||
private static final String DTLS_SRTP_KEY_AGREEMENT_CONSTRAINT = "DtlsSrtpKeyAgreement";
|
|
||||||
private static final String LOCAL_RENDERER_NAME = "Local renderer";
|
private static final String LOCAL_RENDERER_NAME = "Local renderer";
|
||||||
private static final String REMOTE_RENDERER_NAME = "Remote renderer";
|
private static final String REMOTE_RENDERER_NAME = "Remote renderer";
|
||||||
|
|
||||||
@ -130,17 +129,6 @@ public class PeerConnectionClientTest extends InstrumentationTestCase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test instance of the PeerConnectionClient class that overrides the options
|
|
||||||
// for the factory so we can run the test without an Internet connection.
|
|
||||||
class TestPeerConnectionClient extends PeerConnectionClient {
|
|
||||||
protected void configureFactory(PeerConnectionFactory factory) {
|
|
||||||
PeerConnectionFactory.Options options =
|
|
||||||
new PeerConnectionFactory.Options();
|
|
||||||
options.networkIgnoreMask = 0;
|
|
||||||
factory.setOptions(options);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Peer connection events implementation.
|
// Peer connection events implementation.
|
||||||
@Override
|
@Override
|
||||||
public void onLocalDescription(SessionDescription sdp) {
|
public void onLocalDescription(SessionDescription sdp) {
|
||||||
@ -251,33 +239,25 @@ public class PeerConnectionClientTest extends InstrumentationTestCase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private SignalingParameters getTestSignalingParameters() {
|
|
||||||
List<PeerConnection.IceServer> iceServers =
|
|
||||||
new LinkedList<PeerConnection.IceServer>();
|
|
||||||
MediaConstraints pcConstraints = new MediaConstraints();
|
|
||||||
pcConstraints.optional.add(
|
|
||||||
new MediaConstraints.KeyValuePair(DTLS_SRTP_KEY_AGREEMENT_CONSTRAINT, "false"));
|
|
||||||
MediaConstraints videoConstraints = new MediaConstraints();
|
|
||||||
MediaConstraints audioConstraints = new MediaConstraints();
|
|
||||||
SignalingParameters signalingParameters = new SignalingParameters(
|
|
||||||
iceServers, true,
|
|
||||||
pcConstraints, videoConstraints, audioConstraints,
|
|
||||||
null, null, null,
|
|
||||||
null, null);
|
|
||||||
return signalingParameters;
|
|
||||||
}
|
|
||||||
|
|
||||||
PeerConnectionClient createPeerConnectionClient(
|
PeerConnectionClient createPeerConnectionClient(
|
||||||
MockRenderer localRenderer, MockRenderer remoteRenderer,
|
MockRenderer localRenderer, MockRenderer remoteRenderer,
|
||||||
boolean enableVideo, String videoCodec) {
|
boolean enableVideo, String videoCodec) {
|
||||||
SignalingParameters signalingParameters = getTestSignalingParameters();
|
List<PeerConnection.IceServer> iceServers =
|
||||||
|
new LinkedList<PeerConnection.IceServer>();
|
||||||
|
SignalingParameters signalingParameters = new SignalingParameters(
|
||||||
|
iceServers, true, // iceServers, initiator.
|
||||||
|
null, null, null, // clientId, wssUrl, wssPostUrl.
|
||||||
|
null, null); // offerSdp, iceCandidates.
|
||||||
PeerConnectionParameters peerConnectionParameters =
|
PeerConnectionParameters peerConnectionParameters =
|
||||||
new PeerConnectionParameters(
|
new PeerConnectionParameters(
|
||||||
enableVideo, true, // videoCallEnabled, loopback.
|
enableVideo, true, // videoCallEnabled, loopback.
|
||||||
0, 0, 0, 0, videoCodec, true, // video codec parameters.
|
0, 0, 0, 0, videoCodec, true, // video codec parameters.
|
||||||
0, "OPUS", true); // audio codec parameters.
|
0, "OPUS", true); // audio codec parameters.
|
||||||
|
|
||||||
PeerConnectionClient client = new TestPeerConnectionClient();
|
PeerConnectionClient client = PeerConnectionClient.getInstance();
|
||||||
|
PeerConnectionFactory.Options options = new PeerConnectionFactory.Options();
|
||||||
|
options.networkIgnoreMask = 0;
|
||||||
|
client.setPeerConnectionFactoryOptions(options);
|
||||||
client.createPeerConnectionFactory(
|
client.createPeerConnectionFactory(
|
||||||
getInstrumentation().getContext(), null,
|
getInstrumentation().getContext(), null,
|
||||||
peerConnectionParameters, this);
|
peerConnectionParameters, this);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user