Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(152)

Unified Diff: talk/examples/android/src/org/appspot/apprtc/PeerConnectionClient.java

Issue 1235563006: Move talk/examples/* to webrtc/examples. (Closed) Base URL: https://chromium.googlesource.com/external/webrtc.git@master
Patch Set: 201508051337 Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: talk/examples/android/src/org/appspot/apprtc/PeerConnectionClient.java
diff --git a/talk/examples/android/src/org/appspot/apprtc/PeerConnectionClient.java b/talk/examples/android/src/org/appspot/apprtc/PeerConnectionClient.java
deleted file mode 100644
index e3cdc99e239d31355300dd0feed46e1c68f750cc..0000000000000000000000000000000000000000
--- a/talk/examples/android/src/org/appspot/apprtc/PeerConnectionClient.java
+++ /dev/null
@@ -1,1033 +0,0 @@
-/*
- * libjingle
- * Copyright 2014 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.content.Context;
-import android.opengl.EGLContext;
-import android.util.Log;
-
-import org.appspot.apprtc.AppRTCClient.SignalingParameters;
-import org.appspot.apprtc.util.LooperExecutor;
-import org.webrtc.DataChannel;
-import org.webrtc.IceCandidate;
-import org.webrtc.Logging;
-import org.webrtc.MediaCodecVideoEncoder;
-import org.webrtc.MediaConstraints;
-import org.webrtc.MediaConstraints.KeyValuePair;
-import org.webrtc.MediaStream;
-import org.webrtc.PeerConnection;
-import org.webrtc.PeerConnection.IceConnectionState;
-import org.webrtc.PeerConnectionFactory;
-import org.webrtc.SdpObserver;
-import org.webrtc.SessionDescription;
-import org.webrtc.StatsObserver;
-import org.webrtc.StatsReport;
-import org.webrtc.VideoCapturerAndroid;
-import org.webrtc.VideoRenderer;
-import org.webrtc.VideoSource;
-import org.webrtc.VideoTrack;
-
-import java.util.EnumSet;
-import java.util.LinkedList;
-import java.util.Timer;
-import java.util.TimerTask;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * Peer connection client implementation.
- *
- * <p>All public methods are routed to local looper thread.
- * All PeerConnectionEvents callbacks are invoked from the same looper thread.
- * This class is a singleton.
- */
-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 FIELD_TRIAL_VP9 = "WebRTC-SupportVP9/Enabled/";
- private static final String VIDEO_CODEC_VP8 = "VP8";
- private static final String VIDEO_CODEC_VP9 = "VP9";
- private static final String VIDEO_CODEC_H264 = "H264";
- private static final String AUDIO_CODEC_OPUS = "opus";
- private static final String AUDIO_CODEC_ISAC = "ISAC";
- private static final String VIDEO_CODEC_PARAM_START_BITRATE =
- "x-google-start-bitrate";
- private static final String AUDIO_CODEC_PARAM_BITRATE = "maxaveragebitrate";
- private static final String AUDIO_ECHO_CANCELLATION_CONSTRAINT = "googEchoCancellation";
- private static final String AUDIO_AUTO_GAIN_CONTROL_CONSTRAINT= "googAutoGainControl";
- private static final String AUDIO_HIGH_PASS_FILTER_CONSTRAINT = "googHighpassFilter";
- private static final String AUDIO_NOISE_SUPPRESSION_CONSTRAINT = "googNoiseSuppression";
- private static final String MAX_VIDEO_WIDTH_CONSTRAINT = "maxWidth";
- 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 String DTLS_SRTP_KEY_AGREEMENT_CONSTRAINT = "DtlsSrtpKeyAgreement";
- 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 static final PeerConnectionClient instance = new PeerConnectionClient();
- private final PCObserver pcObserver = new PCObserver();
- 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 remoteRender;
- private SignalingParameters signalingParameters;
- private MediaConstraints pcConstraints;
- private MediaConstraints videoConstraints;
- private MediaConstraints audioConstraints;
- private MediaConstraints sdpMediaConstraints;
- private PeerConnectionParameters peerConnectionParameters;
- // Queued remote ICE candidates are consumed only after both local and
- // remote descriptions are set. Similarly local ICE candidates are sent to
- // remote peer after both local and remote description are set.
- private LinkedList<IceCandidate> queuedRemoteCandidates;
- private PeerConnectionEvents events;
- private boolean isInitiator;
- private SessionDescription localSdp; // either offer or answer SDP
- private MediaStream mediaStream;
- private int numberOfCameras;
- private VideoCapturerAndroid videoCapturer;
- // enableVideo is set to true if video should be rendered and sent.
- private boolean renderVideo;
- private VideoTrack localVideoTrack;
- private VideoTrack remoteVideoTrack;
-
- /**
- * Peer connection parameters.
- */
- public static class PeerConnectionParameters {
- public final boolean videoCallEnabled;
- public final boolean loopback;
- public final int videoWidth;
- public final int videoHeight;
- public final int videoFps;
- public final int videoStartBitrate;
- public final String videoCodec;
- public final boolean videoCodecHwAcceleration;
- public final int audioStartBitrate;
- public final String audioCodec;
- public final boolean noAudioProcessing;
- public final boolean cpuOveruseDetection;
-
- public PeerConnectionParameters(
- boolean videoCallEnabled, boolean loopback,
- int videoWidth, int videoHeight, int videoFps, int videoStartBitrate,
- String videoCodec, boolean videoCodecHwAcceleration,
- int audioStartBitrate, String audioCodec,
- boolean noAudioProcessing, boolean cpuOveruseDetection) {
- this.videoCallEnabled = videoCallEnabled;
- this.loopback = loopback;
- this.videoWidth = videoWidth;
- this.videoHeight = videoHeight;
- this.videoFps = videoFps;
- this.videoStartBitrate = videoStartBitrate;
- this.videoCodec = videoCodec;
- this.videoCodecHwAcceleration = videoCodecHwAcceleration;
- this.audioStartBitrate = audioStartBitrate;
- this.audioCodec = audioCodec;
- this.noAudioProcessing = noAudioProcessing;
- this.cpuOveruseDetection = cpuOveruseDetection;
- }
- }
-
- /**
- * Peer connection events.
- */
- public static interface PeerConnectionEvents {
- /**
- * Callback fired once local SDP is created and set.
- */
- public void onLocalDescription(final SessionDescription sdp);
-
- /**
- * Callback fired once local Ice candidate is generated.
- */
- public void onIceCandidate(final IceCandidate candidate);
-
- /**
- * Callback fired once connection is established (IceConnectionState is
- * CONNECTED).
- */
- public void onIceConnected();
-
- /**
- * Callback fired once connection is closed (IceConnectionState is
- * DISCONNECTED).
- */
- public void onIceDisconnected();
-
- /**
- * Callback fired once peer connection is closed.
- */
- public void onPeerConnectionClosed();
-
- /**
- * Callback fired once peer connection statistics is ready.
- */
- public void onPeerConnectionStatsReady(final StatsReport[] reports);
-
- /**
- * Callback fired once peer connection error happened.
- */
- public void onPeerConnectionError(final String description);
- }
-
- private PeerConnectionClient() {
- 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(
- final Context context,
- final EGLContext renderEGLContext,
- final PeerConnectionParameters peerConnectionParameters,
- final PeerConnectionEvents events) {
- this.peerConnectionParameters = peerConnectionParameters;
- this.events = events;
- videoCallEnabled = peerConnectionParameters.videoCallEnabled;
- // 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() {
- @Override
- public void run() {
- createPeerConnectionFactoryInternal(context, renderEGLContext);
- }
- });
- }
-
- public void createPeerConnection(
- final VideoRenderer.Callbacks localRender,
- final VideoRenderer.Callbacks remoteRender,
- final SignalingParameters signalingParameters) {
- if (peerConnectionParameters == null) {
- Log.e(TAG, "Creating peer connection without initializing factory.");
- return;
- }
- this.localRender = localRender;
- this.remoteRender = remoteRender;
- this.signalingParameters = signalingParameters;
- executor.execute(new Runnable() {
- @Override
- public void run() {
- createMediaConstraintsInternal();
- createPeerConnectionInternal();
- }
- });
- }
-
- public void close() {
- executor.execute(new Runnable() {
- @Override
- public void run() {
- closeInternal();
- }
- });
- }
-
- public boolean isVideoCallEnabled() {
- return videoCallEnabled;
- }
-
- private void createPeerConnectionFactoryInternal(
- Context context, EGLContext renderEGLContext) {
- Log.d(TAG, "Create peer connection factory with EGLContext "
- + renderEGLContext + ". Use video: "
- + peerConnectionParameters.videoCallEnabled);
- isError = false;
- // Check if VP9 is used by default.
- if (videoCallEnabled && peerConnectionParameters.videoCodec != null
- && peerConnectionParameters.videoCodec.equals(VIDEO_CODEC_VP9)) {
- PeerConnectionFactory.initializeFieldTrials(FIELD_TRIAL_VP9);
- } else {
- PeerConnectionFactory.initializeFieldTrials(null);
- }
- // Check if H.264 is used by default.
- preferH264 = false;
- if (videoCallEnabled && peerConnectionParameters.videoCodec != null
- && peerConnectionParameters.videoCodec.equals(VIDEO_CODEC_H264)) {
- preferH264 = true;
- }
- // Check if ISAC is used by default.
- preferIsac = false;
- if (peerConnectionParameters.audioCodec != null
- && peerConnectionParameters.audioCodec.equals(AUDIO_CODEC_ISAC)) {
- preferIsac = true;
- }
- if (!PeerConnectionFactory.initializeAndroidGlobals(
- context, true, true,
- peerConnectionParameters.videoCodecHwAcceleration, renderEGLContext)) {
- events.onPeerConnectionError("Failed to initializeAndroidGlobals");
- }
- factory = new PeerConnectionFactory();
- if (options != null) {
- Log.d(TAG, "Factory networkIgnoreMask option: " + options.networkIgnoreMask);
- factory.setOptions(options);
- }
- Log.d(TAG, "Peer connection factory created.");
- }
-
- private void createMediaConstraintsInternal() {
- // Create peer connection constraints.
- pcConstraints = new MediaConstraints();
- // Enable DTLS for normal calls and disable for loopback calls.
- if (peerConnectionParameters.loopback) {
- pcConstraints.optional.add(
- new MediaConstraints.KeyValuePair(DTLS_SRTP_KEY_AGREEMENT_CONSTRAINT, "false"));
- } else {
- pcConstraints.optional.add(
- new MediaConstraints.KeyValuePair(DTLS_SRTP_KEY_AGREEMENT_CONSTRAINT, "true"));
- }
-
- // Check if there is a camera on device and disable video call if not.
- numberOfCameras = VideoCapturerAndroid.getDeviceCount();
- if (numberOfCameras == 0) {
- Log.w(TAG, "No camera on device. Switch to audio only call.");
- videoCallEnabled = false;
- }
- // Create video constraints if video call is enabled.
- if (videoCallEnabled) {
- videoConstraints = new MediaConstraints();
- int videoWidth = peerConnectionParameters.videoWidth;
- int videoHeight = peerConnectionParameters.videoHeight;
-
- // If VP8 HW video encoder is supported and video resolution is not
- // specified force it to HD.
- if ((videoWidth == 0 || videoHeight == 0)
- && peerConnectionParameters.videoCodecHwAcceleration
- && MediaCodecVideoEncoder.isVp8HwSupported()) {
- videoWidth = HD_VIDEO_WIDTH;
- videoHeight = HD_VIDEO_HEIGHT;
- }
-
- // Add video resolution constraints.
- if (videoWidth > 0 && videoHeight > 0) {
- videoWidth = Math.min(videoWidth, MAX_VIDEO_WIDTH);
- videoHeight = Math.min(videoHeight, MAX_VIDEO_HEIGHT);
- videoConstraints.mandatory.add(new KeyValuePair(
- MIN_VIDEO_WIDTH_CONSTRAINT, Integer.toString(videoWidth)));
- videoConstraints.mandatory.add(new KeyValuePair(
- MAX_VIDEO_WIDTH_CONSTRAINT, Integer.toString(videoWidth)));
- videoConstraints.mandatory.add(new KeyValuePair(
- MIN_VIDEO_HEIGHT_CONSTRAINT, Integer.toString(videoHeight)));
- videoConstraints.mandatory.add(new KeyValuePair(
- MAX_VIDEO_HEIGHT_CONSTRAINT, Integer.toString(videoHeight)));
- }
-
- // Add fps constraints.
- int videoFps = peerConnectionParameters.videoFps;
- if (videoFps > 0) {
- videoFps = Math.min(videoFps, MAX_VIDEO_FPS);
- videoConstraints.mandatory.add(new KeyValuePair(
- MIN_VIDEO_FPS_CONSTRAINT, Integer.toString(videoFps)));
- videoConstraints.mandatory.add(new KeyValuePair(
- MAX_VIDEO_FPS_CONSTRAINT, Integer.toString(videoFps)));
- }
- }
-
- // Create audio constraints.
- audioConstraints = new MediaConstraints();
- // added for audio performance measurements
- if (peerConnectionParameters.noAudioProcessing) {
- Log.d(TAG, "Disabling audio processing");
- audioConstraints.mandatory.add(new MediaConstraints.KeyValuePair(
- AUDIO_ECHO_CANCELLATION_CONSTRAINT, "false"));
- audioConstraints.mandatory.add(new MediaConstraints.KeyValuePair(
- AUDIO_AUTO_GAIN_CONTROL_CONSTRAINT, "false"));
- audioConstraints.mandatory.add(new MediaConstraints.KeyValuePair(
- AUDIO_HIGH_PASS_FILTER_CONSTRAINT, "false"));
- audioConstraints.mandatory.add(new MediaConstraints.KeyValuePair(
- AUDIO_NOISE_SUPPRESSION_CONSTRAINT , "false"));
- }
- // Create SDP constraints.
- sdpMediaConstraints = new MediaConstraints();
- sdpMediaConstraints.mandatory.add(new MediaConstraints.KeyValuePair(
- "OfferToReceiveAudio", "true"));
- if (videoCallEnabled || peerConnectionParameters.loopback) {
- sdpMediaConstraints.mandatory.add(new MediaConstraints.KeyValuePair(
- "OfferToReceiveVideo", "true"));
- } else {
- sdpMediaConstraints.mandatory.add(new MediaConstraints.KeyValuePair(
- "OfferToReceiveVideo", "false"));
- }
- }
-
- private void createPeerConnectionInternal() {
- if (factory == null || isError) {
- Log.e(TAG, "Peerconnection factory is not created");
- return;
- }
- Log.d(TAG, "Create peer connection");
- Log.d(TAG, "PCConstraints: " + pcConstraints.toString());
- if (videoConstraints != null) {
- Log.d(TAG, "VideoConstraints: " + videoConstraints.toString());
- }
- queuedRemoteCandidates = new LinkedList<IceCandidate>();
-
- PeerConnection.RTCConfiguration rtcConfig =
- new PeerConnection.RTCConfiguration(signalingParameters.iceServers);
- // TCP candidates are only useful when connecting to a server that supports
- // ICE-TCP.
- rtcConfig.tcpCandidatePolicy = PeerConnection.TcpCandidatePolicy.DISABLED;
- rtcConfig.bundlePolicy = PeerConnection.BundlePolicy.MAXBUNDLE;
- rtcConfig.rtcpMuxPolicy = PeerConnection.RtcpMuxPolicy.REQUIRE;
-
- peerConnection = factory.createPeerConnection(
- rtcConfig, pcConstraints, pcObserver);
- isInitiator = false;
-
- // Set default WebRTC tracing and INFO libjingle logging.
- // NOTE: this _must_ happen while |factory| is alive!
- Logging.enableTracing(
- "logcat:",
- EnumSet.of(Logging.TraceLevel.TRACE_DEFAULT),
- Logging.Severity.LS_INFO);
-
- mediaStream = factory.createLocalMediaStream("ARDAMS");
- if (videoCallEnabled) {
- String cameraDeviceName = VideoCapturerAndroid.getDeviceName(0);
- String frontCameraDeviceName =
- VideoCapturerAndroid.getNameOfFrontFacingDevice();
- if (numberOfCameras > 1 && frontCameraDeviceName != null) {
- cameraDeviceName = frontCameraDeviceName;
- }
- Log.d(TAG, "Opening camera: " + cameraDeviceName);
- videoCapturer = VideoCapturerAndroid.create(cameraDeviceName, null);
- if (videoCapturer == null) {
- reportError("Failed to open camera");
- return;
- }
- mediaStream.addTrack(createVideoTrack(videoCapturer));
- }
-
- mediaStream.addTrack(factory.createAudioTrack(
- AUDIO_TRACK_ID,
- factory.createAudioSource(audioConstraints)));
- peerConnection.addStream(mediaStream);
-
- Log.d(TAG, "Peer connection created.");
- }
-
- private void closeInternal() {
- Log.d(TAG, "Closing peer connection.");
- statsTimer.cancel();
- if (peerConnection != null) {
- peerConnection.dispose();
- peerConnection = null;
- }
- Log.d(TAG, "Closing video source.");
- if (videoSource != null) {
- videoSource.dispose();
- videoSource = null;
- }
- Log.d(TAG, "Closing peer connection factory.");
- if (factory != null) {
- factory.dispose();
- factory = null;
- }
- options = null;
- Log.d(TAG, "Closing peer connection done.");
- events.onPeerConnectionClosed();
- }
-
- public boolean isHDVideo() {
- if (!videoCallEnabled) {
- 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 {
- 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) {
- try {
- statsTimer.schedule(new TimerTask() {
- @Override
- public void run() {
- executor.execute(new Runnable() {
- @Override
- public void run() {
- getStats();
- }
- });
- }
- }, 0, periodMs);
- } catch (Exception e) {
- Log.e(TAG, "Can not schedule statistics timer", e);
- }
- } 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() {
- executor.execute(new Runnable() {
- @Override
- public void run() {
- if (peerConnection != null && !isError) {
- Log.d(TAG, "PC Create OFFER");
- isInitiator = true;
- peerConnection.createOffer(sdpObserver, sdpMediaConstraints);
- }
- }
- });
- }
-
- public void createAnswer() {
- executor.execute(new Runnable() {
- @Override
- public void run() {
- if (peerConnection != null && !isError) {
- Log.d(TAG, "PC create ANSWER");
- isInitiator = false;
- peerConnection.createAnswer(sdpObserver, sdpMediaConstraints);
- }
- }
- });
- }
-
- public void addRemoteIceCandidate(final IceCandidate candidate) {
- executor.execute(new Runnable() {
- @Override
- public void run() {
- if (peerConnection != null && !isError) {
- if (queuedRemoteCandidates != null) {
- queuedRemoteCandidates.add(candidate);
- } else {
- peerConnection.addIceCandidate(candidate);
- }
- }
- }
- });
- }
-
- public void setRemoteDescription(final SessionDescription sdp) {
- executor.execute(new Runnable() {
- @Override
- public void run() {
- if (peerConnection == null || isError) {
- return;
- }
- String sdpDescription = sdp.description;
- if (preferIsac) {
- sdpDescription = preferCodec(sdpDescription, AUDIO_CODEC_ISAC, true);
- }
- if (videoCallEnabled && preferH264) {
- sdpDescription = preferCodec(sdpDescription, VIDEO_CODEC_H264, false);
- }
- if (videoCallEnabled && peerConnectionParameters.videoStartBitrate > 0) {
- sdpDescription = setStartBitrate(VIDEO_CODEC_VP8, true,
- sdpDescription, peerConnectionParameters.videoStartBitrate);
- sdpDescription = setStartBitrate(VIDEO_CODEC_VP9, true,
- sdpDescription, peerConnectionParameters.videoStartBitrate);
- sdpDescription = setStartBitrate(VIDEO_CODEC_H264, true,
- sdpDescription, peerConnectionParameters.videoStartBitrate);
- }
- if (peerConnectionParameters.audioStartBitrate > 0) {
- sdpDescription = setStartBitrate(AUDIO_CODEC_OPUS, false,
- sdpDescription, peerConnectionParameters.audioStartBitrate);
- }
- Log.d(TAG, "Set remote SDP.");
- SessionDescription sdpRemote = new SessionDescription(
- sdp.type, sdpDescription);
- peerConnection.setRemoteDescription(sdpObserver, sdpRemote);
- }
- });
- }
-
- public void stopVideoSource() {
- executor.execute(new Runnable() {
- @Override
- public void run() {
- if (videoSource != null && !videoSourceStopped) {
- Log.d(TAG, "Stop video source.");
- videoSource.stop();
- videoSourceStopped = true;
- }
- }
- });
- }
-
- public void startVideoSource() {
- executor.execute(new Runnable() {
- @Override
- public void run() {
- if (videoSource != null && videoSourceStopped) {
- Log.d(TAG, "Restart video source.");
- videoSource.restart();
- videoSourceStopped = false;
- }
- }
- });
- }
-
- private void reportError(final String errorMessage) {
- Log.e(TAG, "Peerconnection error: " + errorMessage);
- executor.execute(new Runnable() {
- @Override
- public void run() {
- if (!isError) {
- events.onPeerConnectionError(errorMessage);
- isError = true;
- }
- }
- });
- }
-
- private VideoTrack createVideoTrack(VideoCapturerAndroid capturer) {
- videoSource = factory.createVideoSource(capturer, videoConstraints);
-
- localVideoTrack = factory.createVideoTrack(VIDEO_TRACK_ID, videoSource);
- localVideoTrack.setEnabled(renderVideo);
- localVideoTrack.addRenderer(new VideoRenderer(localRender));
- return localVideoTrack;
- }
-
- private static String setStartBitrate(String codec, boolean isVideoCodec,
- String sdpDescription, int bitrateKbps) {
- String[] lines = sdpDescription.split("\r\n");
- int rtpmapLineIndex = -1;
- boolean sdpFormatUpdated = false;
- String codecRtpMap = null;
- // Search for codec rtpmap in format
- // a=rtpmap:<payload type> <encoding name>/<clock rate> [/<encoding parameters>]
- String regex = "^a=rtpmap:(\\d+) " + codec + "(/\\d+)+[\r]?$";
- Pattern codecPattern = Pattern.compile(regex);
- for (int i = 0; i < lines.length; i++) {
- Matcher codecMatcher = codecPattern.matcher(lines[i]);
- if (codecMatcher.matches()) {
- codecRtpMap = codecMatcher.group(1);
- rtpmapLineIndex = i;
- break;
- }
- }
- if (codecRtpMap == null) {
- Log.w(TAG, "No rtpmap for " + codec + " codec");
- return sdpDescription;
- }
- Log.d(TAG, "Found " + codec + " rtpmap " + codecRtpMap
- + " at " + lines[rtpmapLineIndex]);
-
- // Check if a=fmtp string already exist in remote SDP for this codec and
- // update it with new bitrate parameter.
- regex = "^a=fmtp:" + codecRtpMap + " \\w+=\\d+.*[\r]?$";
- codecPattern = Pattern.compile(regex);
- for (int i = 0; i < lines.length; i++) {
- Matcher codecMatcher = codecPattern.matcher(lines[i]);
- if (codecMatcher.matches()) {
- Log.d(TAG, "Found " + codec + " " + lines[i]);
- if (isVideoCodec) {
- lines[i] += "; " + VIDEO_CODEC_PARAM_START_BITRATE
- + "=" + bitrateKbps;
- } else {
- lines[i] += "; " + AUDIO_CODEC_PARAM_BITRATE
- + "=" + (bitrateKbps * 1000);
- }
- Log.d(TAG, "Update remote SDP line: " + lines[i]);
- sdpFormatUpdated = true;
- break;
- }
- }
-
- StringBuilder newSdpDescription = new StringBuilder();
- for (int i = 0; i < lines.length; i++) {
- newSdpDescription.append(lines[i]).append("\r\n");
- // Append new a=fmtp line if no such line exist for a codec.
- if (!sdpFormatUpdated && i == rtpmapLineIndex) {
- String bitrateSet;
- if (isVideoCodec) {
- bitrateSet = "a=fmtp:" + codecRtpMap + " "
- + VIDEO_CODEC_PARAM_START_BITRATE + "=" + bitrateKbps;
- } else {
- bitrateSet = "a=fmtp:" + codecRtpMap + " "
- + AUDIO_CODEC_PARAM_BITRATE + "=" + (bitrateKbps * 1000);
- }
- Log.d(TAG, "Add remote SDP line: " + bitrateSet);
- newSdpDescription.append(bitrateSet).append("\r\n");
- }
-
- }
- return newSdpDescription.toString();
- }
-
- private static String preferCodec(
- String sdpDescription, String codec, boolean isAudio) {
- String[] lines = sdpDescription.split("\r\n");
- int mLineIndex = -1;
- String codecRtpMap = null;
- // a=rtpmap:<payload type> <encoding name>/<clock rate> [/<encoding parameters>]
- String regex = "^a=rtpmap:(\\d+) " + codec + "(/\\d+)+[\r]?$";
- Pattern codecPattern = Pattern.compile(regex);
- String mediaDescription = "m=video ";
- if (isAudio) {
- mediaDescription = "m=audio ";
- }
- for (int i = 0; (i < lines.length)
- && (mLineIndex == -1 || codecRtpMap == null); i++) {
- if (lines[i].startsWith(mediaDescription)) {
- mLineIndex = i;
- continue;
- }
- Matcher codecMatcher = codecPattern.matcher(lines[i]);
- if (codecMatcher.matches()) {
- codecRtpMap = codecMatcher.group(1);
- continue;
- }
- }
- if (mLineIndex == -1) {
- Log.w(TAG, "No " + mediaDescription + " line, so can't prefer " + codec);
- return sdpDescription;
- }
- if (codecRtpMap == null) {
- Log.w(TAG, "No rtpmap for " + codec);
- return sdpDescription;
- }
- Log.d(TAG, "Found " + codec + " rtpmap " + codecRtpMap + ", prefer at "
- + lines[mLineIndex]);
- String[] origMLineParts = lines[mLineIndex].split(" ");
- if (origMLineParts.length > 3) {
- StringBuilder newMLine = new StringBuilder();
- int origPartIndex = 0;
- // Format is: m=<media> <port> <proto> <fmt> ...
- newMLine.append(origMLineParts[origPartIndex++]).append(" ");
- newMLine.append(origMLineParts[origPartIndex++]).append(" ");
- newMLine.append(origMLineParts[origPartIndex++]).append(" ");
- newMLine.append(codecRtpMap);
- for (; origPartIndex < origMLineParts.length; origPartIndex++) {
- if (!origMLineParts[origPartIndex].equals(codecRtpMap)) {
- newMLine.append(" ").append(origMLineParts[origPartIndex]);
- }
- }
- lines[mLineIndex] = newMLine.toString();
- Log.d(TAG, "Change media description: " + lines[mLineIndex]);
- } else {
- Log.e(TAG, "Wrong SDP media description format: " + lines[mLineIndex]);
- }
- StringBuilder newSdpDescription = new StringBuilder();
- for (String line : lines) {
- newSdpDescription.append(line).append("\r\n");
- }
- return newSdpDescription.toString();
- }
-
- private void drainCandidates() {
- if (queuedRemoteCandidates != null) {
- Log.d(TAG, "Add " + queuedRemoteCandidates.size() + " remote candidates");
- for (IceCandidate candidate : queuedRemoteCandidates) {
- peerConnection.addIceCandidate(candidate);
- }
- queuedRemoteCandidates = null;
- }
- }
-
- private void switchCameraInternal() {
- if (!videoCallEnabled || numberOfCameras < 2 || isError || videoCapturer == null) {
- 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");
- videoCapturer.switchCamera(null);
- }
-
- public void switchCamera() {
- executor.execute(new Runnable() {
- @Override
- public void run() {
- switchCameraInternal();
- }
- });
- }
-
- // Implementation detail: observe ICE & stream changes and react accordingly.
- private class PCObserver implements PeerConnection.Observer {
- @Override
- public void onIceCandidate(final IceCandidate candidate){
- executor.execute(new Runnable() {
- @Override
- public void run() {
- events.onIceCandidate(candidate);
- }
- });
- }
-
- @Override
- public void onSignalingChange(
- PeerConnection.SignalingState newState) {
- Log.d(TAG, "SignalingState: " + newState);
- }
-
- @Override
- public void onIceConnectionChange(
- final PeerConnection.IceConnectionState newState) {
- executor.execute(new Runnable() {
- @Override
- public void run() {
- Log.d(TAG, "IceConnectionState: " + newState);
- if (newState == IceConnectionState.CONNECTED) {
- events.onIceConnected();
- } else if (newState == IceConnectionState.DISCONNECTED) {
- events.onIceDisconnected();
- } else if (newState == IceConnectionState.FAILED) {
- reportError("ICE connection failed.");
- }
- }
- });
- }
-
- @Override
- public void onIceGatheringChange(
- PeerConnection.IceGatheringState newState) {
- Log.d(TAG, "IceGatheringState: " + newState);
- }
-
- @Override
- public void onIceConnectionReceivingChange(boolean receiving) {
- Log.d(TAG, "IceConnectionReceiving changed to " + receiving);
- }
-
- @Override
- public void onAddStream(final MediaStream stream){
- executor.execute(new Runnable() {
- @Override
- public void run() {
- if (peerConnection == null || isError) {
- return;
- }
- if (stream.audioTracks.size() > 1 || stream.videoTracks.size() > 1) {
- reportError("Weird-looking stream: " + stream);
- return;
- }
- if (stream.videoTracks.size() == 1) {
- remoteVideoTrack = stream.videoTracks.get(0);
- remoteVideoTrack.setEnabled(renderVideo);
- remoteVideoTrack.addRenderer(new VideoRenderer(remoteRender));
- }
- }
- });
- }
-
- @Override
- public void onRemoveStream(final MediaStream stream){
- executor.execute(new Runnable() {
- @Override
- public void run() {
- if (peerConnection == null || isError) {
- return;
- }
- remoteVideoTrack = null;
- stream.videoTracks.get(0).dispose();
- }
- });
- }
-
- @Override
- public void onDataChannel(final DataChannel dc) {
- reportError("AppRTC doesn't use data channels, but got: " + dc.label()
- + " anyway!");
- }
-
- @Override
- public void onRenegotiationNeeded() {
- // No need to do anything; AppRTC follows a pre-agreed-upon
- // signaling/negotiation protocol.
- }
- }
-
- // Implementation detail: handle offer creation/signaling and answer setting,
- // as well as adding remote ICE candidates once the answer SDP is set.
- private class SDPObserver implements SdpObserver {
- @Override
- public void onCreateSuccess(final SessionDescription origSdp) {
- if (localSdp != null) {
- reportError("Multiple SDP create.");
- return;
- }
- String sdpDescription = origSdp.description;
- if (preferIsac) {
- sdpDescription = preferCodec(sdpDescription, AUDIO_CODEC_ISAC, true);
- }
- if (videoCallEnabled && preferH264) {
- sdpDescription = preferCodec(sdpDescription, VIDEO_CODEC_H264, false);
- }
- final SessionDescription sdp = new SessionDescription(
- origSdp.type, sdpDescription);
- localSdp = sdp;
- executor.execute(new Runnable() {
- @Override
- public void run() {
- if (peerConnection != null && !isError) {
- Log.d(TAG, "Set local SDP from " + sdp.type);
- peerConnection.setLocalDescription(sdpObserver, sdp);
- }
- }
- });
- }
-
- @Override
- public void onSetSuccess() {
- executor.execute(new Runnable() {
- @Override
- public void run() {
- if (peerConnection == null || isError) {
- return;
- }
- if (isInitiator) {
- // For offering peer connection we first create offer and set
- // local SDP, then after receiving answer set remote SDP.
- if (peerConnection.getRemoteDescription() == null) {
- // We've just set our local SDP so time to send it.
- Log.d(TAG, "Local SDP set succesfully");
- events.onLocalDescription(localSdp);
- } else {
- // We've just set remote description, so drain remote
- // and send local ICE candidates.
- Log.d(TAG, "Remote SDP set succesfully");
- drainCandidates();
- }
- } else {
- // For answering peer connection we set remote SDP and then
- // create answer and set local SDP.
- if (peerConnection.getLocalDescription() != null) {
- // We've just set our local SDP so time to send it, drain
- // remote and send local ICE candidates.
- Log.d(TAG, "Local SDP set succesfully");
- events.onLocalDescription(localSdp);
- drainCandidates();
- } else {
- // We've just set remote SDP - do nothing for now -
- // answer will be created soon.
- Log.d(TAG, "Remote SDP set succesfully");
- }
- }
- }
- });
- }
-
- @Override
- public void onCreateFailure(final String error) {
- reportError("createSDP error: " + error);
- }
-
- @Override
- public void onSetFailure(final String error) {
- reportError("setSDP error: " + error);
- }
- }
-}

Powered by Google App Engine
This is Rietveld 408576698