| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * libjingle | |
| 3 * Copyright 2015 Google Inc. | |
| 4 * | |
| 5 * Redistribution and use in source and binary forms, with or without | |
| 6 * modification, are permitted provided that the following conditions are met: | |
| 7 * | |
| 8 * 1. Redistributions of source code must retain the above copyright notice, | |
| 9 * this list of conditions and the following disclaimer. | |
| 10 * 2. Redistributions in binary form must reproduce the above copyright notice, | |
| 11 * this list of conditions and the following disclaimer in the documentation | |
| 12 * and/or other materials provided with the distribution. | |
| 13 * 3. The name of the author may not be used to endorse or promote products | |
| 14 * derived from this software without specific prior written permission. | |
| 15 * | |
| 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED | |
| 17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
| 18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | |
| 19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
| 20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
| 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | |
| 22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | |
| 23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | |
| 24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF | |
| 25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| 26 */ | |
| 27 | |
| 28 package org.appspot.apprtc; | |
| 29 | |
| 30 import org.appspot.apprtc.AppRTCClient.RoomConnectionParameters; | |
| 31 import org.appspot.apprtc.AppRTCClient.SignalingParameters; | |
| 32 import org.appspot.apprtc.PeerConnectionClient.PeerConnectionParameters; | |
| 33 import org.appspot.apprtc.util.LooperExecutor; | |
| 34 | |
| 35 import android.app.Activity; | |
| 36 import android.app.AlertDialog; | |
| 37 import android.app.FragmentTransaction; | |
| 38 import android.content.DialogInterface; | |
| 39 import android.content.Intent; | |
| 40 import android.content.pm.PackageManager; | |
| 41 import android.net.Uri; | |
| 42 import android.opengl.GLSurfaceView; | |
| 43 import android.os.Bundle; | |
| 44 import android.util.Log; | |
| 45 import android.view.View; | |
| 46 import android.view.Window; | |
| 47 import android.view.WindowManager.LayoutParams; | |
| 48 import android.widget.Toast; | |
| 49 | |
| 50 import org.webrtc.IceCandidate; | |
| 51 import org.webrtc.SessionDescription; | |
| 52 import org.webrtc.StatsReport; | |
| 53 import org.webrtc.VideoRenderer; | |
| 54 import org.webrtc.VideoRendererGui; | |
| 55 import org.webrtc.VideoRendererGui.ScalingType; | |
| 56 | |
| 57 /** | |
| 58 * Activity for peer connection call setup, call waiting | |
| 59 * and call view. | |
| 60 */ | |
| 61 public class CallActivity extends Activity | |
| 62 implements AppRTCClient.SignalingEvents, | |
| 63 PeerConnectionClient.PeerConnectionEvents, | |
| 64 CallFragment.OnCallEvents { | |
| 65 | |
| 66 public static final String EXTRA_ROOMID = | |
| 67 "org.appspot.apprtc.ROOMID"; | |
| 68 public static final String EXTRA_LOOPBACK = | |
| 69 "org.appspot.apprtc.LOOPBACK"; | |
| 70 public static final String EXTRA_VIDEO_CALL = | |
| 71 "org.appspot.apprtc.VIDEO_CALL"; | |
| 72 public static final String EXTRA_VIDEO_WIDTH = | |
| 73 "org.appspot.apprtc.VIDEO_WIDTH"; | |
| 74 public static final String EXTRA_VIDEO_HEIGHT = | |
| 75 "org.appspot.apprtc.VIDEO_HEIGHT"; | |
| 76 public static final String EXTRA_VIDEO_FPS = | |
| 77 "org.appspot.apprtc.VIDEO_FPS"; | |
| 78 public static final String EXTRA_VIDEO_BITRATE = | |
| 79 "org.appspot.apprtc.VIDEO_BITRATE"; | |
| 80 public static final String EXTRA_VIDEOCODEC = | |
| 81 "org.appspot.apprtc.VIDEOCODEC"; | |
| 82 public static final String EXTRA_HWCODEC_ENABLED = | |
| 83 "org.appspot.apprtc.HWCODEC"; | |
| 84 public static final String EXTRA_AUDIO_BITRATE = | |
| 85 "org.appspot.apprtc.AUDIO_BITRATE"; | |
| 86 public static final String EXTRA_AUDIOCODEC = | |
| 87 "org.appspot.apprtc.AUDIOCODEC"; | |
| 88 public static final String EXTRA_NOAUDIOPROCESSING_ENABLED = | |
| 89 "org.appspot.apprtc.NOAUDIOPROCESSING"; | |
| 90 public static final String EXTRA_CPUOVERUSE_DETECTION = | |
| 91 "org.appspot.apprtc.CPUOVERUSE_DETECTION"; | |
| 92 public static final String EXTRA_DISPLAY_HUD = | |
| 93 "org.appspot.apprtc.DISPLAY_HUD"; | |
| 94 public static final String EXTRA_CMDLINE = | |
| 95 "org.appspot.apprtc.CMDLINE"; | |
| 96 public static final String EXTRA_RUNTIME = | |
| 97 "org.appspot.apprtc.RUNTIME"; | |
| 98 private static final String TAG = "CallRTCClient"; | |
| 99 | |
| 100 // List of mandatory application permissions. | |
| 101 private static final String[] MANDATORY_PERMISSIONS = { | |
| 102 "android.permission.MODIFY_AUDIO_SETTINGS", | |
| 103 "android.permission.RECORD_AUDIO", | |
| 104 "android.permission.INTERNET" | |
| 105 }; | |
| 106 | |
| 107 // Peer connection statistics callback period in ms. | |
| 108 private static final int STAT_CALLBACK_PERIOD = 1000; | |
| 109 // Local preview screen position before call is connected. | |
| 110 private static final int LOCAL_X_CONNECTING = 0; | |
| 111 private static final int LOCAL_Y_CONNECTING = 0; | |
| 112 private static final int LOCAL_WIDTH_CONNECTING = 100; | |
| 113 private static final int LOCAL_HEIGHT_CONNECTING = 100; | |
| 114 // Local preview screen position after call is connected. | |
| 115 private static final int LOCAL_X_CONNECTED = 72; | |
| 116 private static final int LOCAL_Y_CONNECTED = 72; | |
| 117 private static final int LOCAL_WIDTH_CONNECTED = 25; | |
| 118 private static final int LOCAL_HEIGHT_CONNECTED = 25; | |
| 119 // Remote video screen position | |
| 120 private static final int REMOTE_X = 0; | |
| 121 private static final int REMOTE_Y = 0; | |
| 122 private static final int REMOTE_WIDTH = 100; | |
| 123 private static final int REMOTE_HEIGHT = 100; | |
| 124 | |
| 125 private PeerConnectionClient peerConnectionClient = null; | |
| 126 private AppRTCClient appRtcClient; | |
| 127 private SignalingParameters signalingParameters; | |
| 128 private AppRTCAudioManager audioManager = null; | |
| 129 private VideoRenderer.Callbacks localRender; | |
| 130 private VideoRenderer.Callbacks remoteRender; | |
| 131 private ScalingType scalingType; | |
| 132 private Toast logToast; | |
| 133 private boolean commandLineRun; | |
| 134 private int runTimeMs; | |
| 135 private boolean activityRunning; | |
| 136 private RoomConnectionParameters roomConnectionParameters; | |
| 137 private PeerConnectionParameters peerConnectionParameters; | |
| 138 private boolean iceConnected; | |
| 139 private boolean isError; | |
| 140 private boolean callControlFragmentVisible = true; | |
| 141 private long callStartedTimeMs = 0; | |
| 142 | |
| 143 // Controls | |
| 144 private GLSurfaceView videoView; | |
| 145 CallFragment callFragment; | |
| 146 HudFragment hudFragment; | |
| 147 | |
| 148 @Override | |
| 149 public void onCreate(Bundle savedInstanceState) { | |
| 150 super.onCreate(savedInstanceState); | |
| 151 Thread.setDefaultUncaughtExceptionHandler( | |
| 152 new UnhandledExceptionHandler(this)); | |
| 153 | |
| 154 // Set window styles for fullscreen-window size. Needs to be done before | |
| 155 // adding content. | |
| 156 requestWindowFeature(Window.FEATURE_NO_TITLE); | |
| 157 getWindow().addFlags( | |
| 158 LayoutParams.FLAG_FULLSCREEN | |
| 159 | LayoutParams.FLAG_KEEP_SCREEN_ON | |
| 160 | LayoutParams.FLAG_DISMISS_KEYGUARD | |
| 161 | LayoutParams.FLAG_SHOW_WHEN_LOCKED | |
| 162 | LayoutParams.FLAG_TURN_SCREEN_ON); | |
| 163 getWindow().getDecorView().setSystemUiVisibility( | |
| 164 View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | |
| 165 | View.SYSTEM_UI_FLAG_FULLSCREEN | |
| 166 | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); | |
| 167 setContentView(R.layout.activity_call); | |
| 168 | |
| 169 iceConnected = false; | |
| 170 signalingParameters = null; | |
| 171 scalingType = ScalingType.SCALE_ASPECT_FILL; | |
| 172 | |
| 173 // Create UI controls. | |
| 174 videoView = (GLSurfaceView) findViewById(R.id.glview_call); | |
| 175 callFragment = new CallFragment(); | |
| 176 hudFragment = new HudFragment(); | |
| 177 | |
| 178 // Create video renderers. | |
| 179 VideoRendererGui.setView(videoView, new Runnable() { | |
| 180 @Override | |
| 181 public void run() { | |
| 182 createPeerConnectionFactory(); | |
| 183 } | |
| 184 }); | |
| 185 remoteRender = VideoRendererGui.create( | |
| 186 REMOTE_X, REMOTE_Y, | |
| 187 REMOTE_WIDTH, REMOTE_HEIGHT, scalingType, false); | |
| 188 localRender = VideoRendererGui.create( | |
| 189 LOCAL_X_CONNECTING, LOCAL_Y_CONNECTING, | |
| 190 LOCAL_WIDTH_CONNECTING, LOCAL_HEIGHT_CONNECTING, scalingType, true); | |
| 191 | |
| 192 // Show/hide call control fragment on view click. | |
| 193 videoView.setOnClickListener(new View.OnClickListener() { | |
| 194 @Override | |
| 195 public void onClick(View view) { | |
| 196 toggleCallControlFragmentVisibility(); | |
| 197 } | |
| 198 }); | |
| 199 | |
| 200 // Check for mandatory permissions. | |
| 201 for (String permission : MANDATORY_PERMISSIONS) { | |
| 202 if (checkCallingOrSelfPermission(permission) != PackageManager.PERMISSION_
GRANTED) { | |
| 203 logAndToast("Permission " + permission + " is not granted"); | |
| 204 setResult(RESULT_CANCELED); | |
| 205 finish(); | |
| 206 return; | |
| 207 } | |
| 208 } | |
| 209 | |
| 210 // Get Intent parameters. | |
| 211 final Intent intent = getIntent(); | |
| 212 Uri roomUri = intent.getData(); | |
| 213 if (roomUri == null) { | |
| 214 logAndToast(getString(R.string.missing_url)); | |
| 215 Log.e(TAG, "Didn't get any URL in intent!"); | |
| 216 setResult(RESULT_CANCELED); | |
| 217 finish(); | |
| 218 return; | |
| 219 } | |
| 220 String roomId = intent.getStringExtra(EXTRA_ROOMID); | |
| 221 if (roomId == null || roomId.length() == 0) { | |
| 222 logAndToast(getString(R.string.missing_url)); | |
| 223 Log.e(TAG, "Incorrect room ID in intent!"); | |
| 224 setResult(RESULT_CANCELED); | |
| 225 finish(); | |
| 226 return; | |
| 227 } | |
| 228 boolean loopback = intent.getBooleanExtra(EXTRA_LOOPBACK, false); | |
| 229 peerConnectionParameters = new PeerConnectionParameters( | |
| 230 intent.getBooleanExtra(EXTRA_VIDEO_CALL, true), | |
| 231 loopback, | |
| 232 intent.getIntExtra(EXTRA_VIDEO_WIDTH, 0), | |
| 233 intent.getIntExtra(EXTRA_VIDEO_HEIGHT, 0), | |
| 234 intent.getIntExtra(EXTRA_VIDEO_FPS, 0), | |
| 235 intent.getIntExtra(EXTRA_VIDEO_BITRATE, 0), | |
| 236 intent.getStringExtra(EXTRA_VIDEOCODEC), | |
| 237 intent.getBooleanExtra(EXTRA_HWCODEC_ENABLED, true), | |
| 238 intent.getIntExtra(EXTRA_AUDIO_BITRATE, 0), | |
| 239 intent.getStringExtra(EXTRA_AUDIOCODEC), | |
| 240 intent.getBooleanExtra(EXTRA_NOAUDIOPROCESSING_ENABLED, false), | |
| 241 intent.getBooleanExtra(EXTRA_CPUOVERUSE_DETECTION, true)); | |
| 242 commandLineRun = intent.getBooleanExtra(EXTRA_CMDLINE, false); | |
| 243 runTimeMs = intent.getIntExtra(EXTRA_RUNTIME, 0); | |
| 244 | |
| 245 // Create connection client and connection parameters. | |
| 246 appRtcClient = new WebSocketRTCClient(this, new LooperExecutor()); | |
| 247 roomConnectionParameters = new RoomConnectionParameters( | |
| 248 roomUri.toString(), roomId, loopback); | |
| 249 | |
| 250 // Send intent arguments to fragments. | |
| 251 callFragment.setArguments(intent.getExtras()); | |
| 252 hudFragment.setArguments(intent.getExtras()); | |
| 253 // Activate call and HUD fragments and start the call. | |
| 254 FragmentTransaction ft = getFragmentManager().beginTransaction(); | |
| 255 ft.add(R.id.call_fragment_container, callFragment); | |
| 256 ft.add(R.id.hud_fragment_container, hudFragment); | |
| 257 ft.commit(); | |
| 258 startCall(); | |
| 259 | |
| 260 // For command line execution run connection for <runTimeMs> and exit. | |
| 261 if (commandLineRun && runTimeMs > 0) { | |
| 262 videoView.postDelayed(new Runnable() { | |
| 263 public void run() { | |
| 264 disconnect(); | |
| 265 } | |
| 266 }, runTimeMs); | |
| 267 } | |
| 268 } | |
| 269 | |
| 270 // Activity interfaces | |
| 271 @Override | |
| 272 public void onPause() { | |
| 273 super.onPause(); | |
| 274 videoView.onPause(); | |
| 275 activityRunning = false; | |
| 276 if (peerConnectionClient != null) { | |
| 277 peerConnectionClient.stopVideoSource(); | |
| 278 } | |
| 279 } | |
| 280 | |
| 281 @Override | |
| 282 public void onResume() { | |
| 283 super.onResume(); | |
| 284 videoView.onResume(); | |
| 285 activityRunning = true; | |
| 286 if (peerConnectionClient != null) { | |
| 287 peerConnectionClient.startVideoSource(); | |
| 288 } | |
| 289 } | |
| 290 | |
| 291 @Override | |
| 292 protected void onDestroy() { | |
| 293 disconnect(); | |
| 294 super.onDestroy(); | |
| 295 if (logToast != null) { | |
| 296 logToast.cancel(); | |
| 297 } | |
| 298 activityRunning = false; | |
| 299 } | |
| 300 | |
| 301 // CallFragment.OnCallEvents interface implementation. | |
| 302 @Override | |
| 303 public void onCallHangUp() { | |
| 304 disconnect(); | |
| 305 } | |
| 306 | |
| 307 @Override | |
| 308 public void onCameraSwitch() { | |
| 309 if (peerConnectionClient != null) { | |
| 310 peerConnectionClient.switchCamera(); | |
| 311 } | |
| 312 } | |
| 313 | |
| 314 @Override | |
| 315 public void onVideoScalingSwitch(ScalingType scalingType) { | |
| 316 this.scalingType = scalingType; | |
| 317 updateVideoView(); | |
| 318 } | |
| 319 | |
| 320 // Helper functions. | |
| 321 private void toggleCallControlFragmentVisibility() { | |
| 322 if (!iceConnected || !callFragment.isAdded()) { | |
| 323 return; | |
| 324 } | |
| 325 // Show/hide call control fragment | |
| 326 callControlFragmentVisible = !callControlFragmentVisible; | |
| 327 FragmentTransaction ft = getFragmentManager().beginTransaction(); | |
| 328 if (callControlFragmentVisible) { | |
| 329 ft.show(callFragment); | |
| 330 ft.show(hudFragment); | |
| 331 } else { | |
| 332 ft.hide(callFragment); | |
| 333 ft.hide(hudFragment); | |
| 334 } | |
| 335 ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE); | |
| 336 ft.commit(); | |
| 337 } | |
| 338 | |
| 339 private void updateVideoView() { | |
| 340 VideoRendererGui.update(remoteRender, | |
| 341 REMOTE_X, REMOTE_Y, | |
| 342 REMOTE_WIDTH, REMOTE_HEIGHT, scalingType, false); | |
| 343 if (iceConnected) { | |
| 344 VideoRendererGui.update(localRender, | |
| 345 LOCAL_X_CONNECTED, LOCAL_Y_CONNECTED, | |
| 346 LOCAL_WIDTH_CONNECTED, LOCAL_HEIGHT_CONNECTED, | |
| 347 ScalingType.SCALE_ASPECT_FIT, true); | |
| 348 } else { | |
| 349 VideoRendererGui.update(localRender, | |
| 350 LOCAL_X_CONNECTING, LOCAL_Y_CONNECTING, | |
| 351 LOCAL_WIDTH_CONNECTING, LOCAL_HEIGHT_CONNECTING, scalingType, true); | |
| 352 } | |
| 353 } | |
| 354 | |
| 355 private void startCall() { | |
| 356 if (appRtcClient == null) { | |
| 357 Log.e(TAG, "AppRTC client is not allocated for a call."); | |
| 358 return; | |
| 359 } | |
| 360 callStartedTimeMs = System.currentTimeMillis(); | |
| 361 | |
| 362 // Start room connection. | |
| 363 logAndToast(getString(R.string.connecting_to, | |
| 364 roomConnectionParameters.roomUrl)); | |
| 365 appRtcClient.connectToRoom(roomConnectionParameters); | |
| 366 | |
| 367 // Create and audio manager that will take care of audio routing, | |
| 368 // audio modes, audio device enumeration etc. | |
| 369 audioManager = AppRTCAudioManager.create(this, new Runnable() { | |
| 370 // This method will be called each time the audio state (number and | |
| 371 // type of devices) has been changed. | |
| 372 @Override | |
| 373 public void run() { | |
| 374 onAudioManagerChangedState(); | |
| 375 } | |
| 376 } | |
| 377 ); | |
| 378 // Store existing audio settings and change audio mode to | |
| 379 // MODE_IN_COMMUNICATION for best possible VoIP performance. | |
| 380 Log.d(TAG, "Initializing the audio manager..."); | |
| 381 audioManager.init(); | |
| 382 } | |
| 383 | |
| 384 // Should be called from UI thread | |
| 385 private void callConnected() { | |
| 386 final long delta = System.currentTimeMillis() - callStartedTimeMs; | |
| 387 Log.i(TAG, "Call connected: delay=" + delta + "ms"); | |
| 388 | |
| 389 // Update video view. | |
| 390 updateVideoView(); | |
| 391 // Enable statistics callback. | |
| 392 peerConnectionClient.enableStatsEvents(true, STAT_CALLBACK_PERIOD); | |
| 393 } | |
| 394 | |
| 395 private void onAudioManagerChangedState() { | |
| 396 // TODO(henrika): disable video if AppRTCAudioManager.AudioDevice.EARPIECE | |
| 397 // is active. | |
| 398 } | |
| 399 | |
| 400 // Create peer connection factory when EGL context is ready. | |
| 401 private void createPeerConnectionFactory() { | |
| 402 runOnUiThread(new Runnable() { | |
| 403 @Override | |
| 404 public void run() { | |
| 405 if (peerConnectionClient == null) { | |
| 406 final long delta = System.currentTimeMillis() - callStartedTimeMs; | |
| 407 Log.d(TAG, "Creating peer connection factory, delay=" + delta + "ms"); | |
| 408 peerConnectionClient = PeerConnectionClient.getInstance(); | |
| 409 peerConnectionClient.createPeerConnectionFactory(CallActivity.this, | |
| 410 VideoRendererGui.getEGLContext(), peerConnectionParameters, | |
| 411 CallActivity.this); | |
| 412 } | |
| 413 if (signalingParameters != null) { | |
| 414 Log.w(TAG, "EGL context is ready after room connection."); | |
| 415 onConnectedToRoomInternal(signalingParameters); | |
| 416 } | |
| 417 } | |
| 418 }); | |
| 419 } | |
| 420 | |
| 421 // Disconnect from remote resources, dispose of local resources, and exit. | |
| 422 private void disconnect() { | |
| 423 activityRunning = false; | |
| 424 if (appRtcClient != null) { | |
| 425 appRtcClient.disconnectFromRoom(); | |
| 426 appRtcClient = null; | |
| 427 } | |
| 428 if (peerConnectionClient != null) { | |
| 429 peerConnectionClient.close(); | |
| 430 peerConnectionClient = null; | |
| 431 } | |
| 432 if (audioManager != null) { | |
| 433 audioManager.close(); | |
| 434 audioManager = null; | |
| 435 } | |
| 436 if (iceConnected && !isError) { | |
| 437 setResult(RESULT_OK); | |
| 438 } else { | |
| 439 setResult(RESULT_CANCELED); | |
| 440 } | |
| 441 finish(); | |
| 442 } | |
| 443 | |
| 444 private void disconnectWithErrorMessage(final String errorMessage) { | |
| 445 if (commandLineRun || !activityRunning) { | |
| 446 Log.e(TAG, "Critical error: " + errorMessage); | |
| 447 disconnect(); | |
| 448 } else { | |
| 449 new AlertDialog.Builder(this) | |
| 450 .setTitle(getText(R.string.channel_error_title)) | |
| 451 .setMessage(errorMessage) | |
| 452 .setCancelable(false) | |
| 453 .setNeutralButton(R.string.ok, new DialogInterface.OnClickListener() { | |
| 454 @Override | |
| 455 public void onClick(DialogInterface dialog, int id) { | |
| 456 dialog.cancel(); | |
| 457 disconnect(); | |
| 458 } | |
| 459 }).create().show(); | |
| 460 } | |
| 461 } | |
| 462 | |
| 463 // Log |msg| and Toast about it. | |
| 464 private void logAndToast(String msg) { | |
| 465 Log.d(TAG, msg); | |
| 466 if (logToast != null) { | |
| 467 logToast.cancel(); | |
| 468 } | |
| 469 logToast = Toast.makeText(this, msg, Toast.LENGTH_SHORT); | |
| 470 logToast.show(); | |
| 471 } | |
| 472 | |
| 473 private void reportError(final String description) { | |
| 474 runOnUiThread(new Runnable() { | |
| 475 @Override | |
| 476 public void run() { | |
| 477 if (!isError) { | |
| 478 isError = true; | |
| 479 disconnectWithErrorMessage(description); | |
| 480 } | |
| 481 } | |
| 482 }); | |
| 483 } | |
| 484 | |
| 485 // -----Implementation of AppRTCClient.AppRTCSignalingEvents --------------- | |
| 486 // All callbacks are invoked from websocket signaling looper thread and | |
| 487 // are routed to UI thread. | |
| 488 private void onConnectedToRoomInternal(final SignalingParameters params) { | |
| 489 final long delta = System.currentTimeMillis() - callStartedTimeMs; | |
| 490 | |
| 491 signalingParameters = params; | |
| 492 if (peerConnectionClient == null) { | |
| 493 Log.w(TAG, "Room is connected, but EGL context is not ready yet."); | |
| 494 return; | |
| 495 } | |
| 496 logAndToast("Creating peer connection, delay=" + delta + "ms"); | |
| 497 peerConnectionClient.createPeerConnection( | |
| 498 localRender, remoteRender, signalingParameters); | |
| 499 | |
| 500 if (signalingParameters.initiator) { | |
| 501 logAndToast("Creating OFFER..."); | |
| 502 // Create offer. Offer SDP will be sent to answering client in | |
| 503 // PeerConnectionEvents.onLocalDescription event. | |
| 504 peerConnectionClient.createOffer(); | |
| 505 } else { | |
| 506 if (params.offerSdp != null) { | |
| 507 peerConnectionClient.setRemoteDescription(params.offerSdp); | |
| 508 logAndToast("Creating ANSWER..."); | |
| 509 // Create answer. Answer SDP will be sent to offering client in | |
| 510 // PeerConnectionEvents.onLocalDescription event. | |
| 511 peerConnectionClient.createAnswer(); | |
| 512 } | |
| 513 if (params.iceCandidates != null) { | |
| 514 // Add remote ICE candidates from room. | |
| 515 for (IceCandidate iceCandidate : params.iceCandidates) { | |
| 516 peerConnectionClient.addRemoteIceCandidate(iceCandidate); | |
| 517 } | |
| 518 } | |
| 519 } | |
| 520 } | |
| 521 | |
| 522 @Override | |
| 523 public void onConnectedToRoom(final SignalingParameters params) { | |
| 524 runOnUiThread(new Runnable() { | |
| 525 @Override | |
| 526 public void run() { | |
| 527 onConnectedToRoomInternal(params); | |
| 528 } | |
| 529 }); | |
| 530 } | |
| 531 | |
| 532 @Override | |
| 533 public void onRemoteDescription(final SessionDescription sdp) { | |
| 534 final long delta = System.currentTimeMillis() - callStartedTimeMs; | |
| 535 runOnUiThread(new Runnable() { | |
| 536 @Override | |
| 537 public void run() { | |
| 538 if (peerConnectionClient == null) { | |
| 539 Log.e(TAG, "Received remote SDP for non-initilized peer connection."); | |
| 540 return; | |
| 541 } | |
| 542 logAndToast("Received remote " + sdp.type + ", delay=" + delta + "ms"); | |
| 543 peerConnectionClient.setRemoteDescription(sdp); | |
| 544 if (!signalingParameters.initiator) { | |
| 545 logAndToast("Creating ANSWER..."); | |
| 546 // Create answer. Answer SDP will be sent to offering client in | |
| 547 // PeerConnectionEvents.onLocalDescription event. | |
| 548 peerConnectionClient.createAnswer(); | |
| 549 } | |
| 550 } | |
| 551 }); | |
| 552 } | |
| 553 | |
| 554 @Override | |
| 555 public void onRemoteIceCandidate(final IceCandidate candidate) { | |
| 556 runOnUiThread(new Runnable() { | |
| 557 @Override | |
| 558 public void run() { | |
| 559 if (peerConnectionClient == null) { | |
| 560 Log.e(TAG, | |
| 561 "Received ICE candidate for non-initilized peer connection."); | |
| 562 return; | |
| 563 } | |
| 564 peerConnectionClient.addRemoteIceCandidate(candidate); | |
| 565 } | |
| 566 }); | |
| 567 } | |
| 568 | |
| 569 @Override | |
| 570 public void onChannelClose() { | |
| 571 runOnUiThread(new Runnable() { | |
| 572 @Override | |
| 573 public void run() { | |
| 574 logAndToast("Remote end hung up; dropping PeerConnection"); | |
| 575 disconnect(); | |
| 576 } | |
| 577 }); | |
| 578 } | |
| 579 | |
| 580 @Override | |
| 581 public void onChannelError(final String description) { | |
| 582 reportError(description); | |
| 583 } | |
| 584 | |
| 585 // -----Implementation of PeerConnectionClient.PeerConnectionEvents.--------- | |
| 586 // Send local peer connection SDP and ICE candidates to remote party. | |
| 587 // All callbacks are invoked from peer connection client looper thread and | |
| 588 // are routed to UI thread. | |
| 589 @Override | |
| 590 public void onLocalDescription(final SessionDescription sdp) { | |
| 591 final long delta = System.currentTimeMillis() - callStartedTimeMs; | |
| 592 runOnUiThread(new Runnable() { | |
| 593 @Override | |
| 594 public void run() { | |
| 595 if (appRtcClient != null) { | |
| 596 logAndToast("Sending " + sdp.type + ", delay=" + delta + "ms"); | |
| 597 if (signalingParameters.initiator) { | |
| 598 appRtcClient.sendOfferSdp(sdp); | |
| 599 } else { | |
| 600 appRtcClient.sendAnswerSdp(sdp); | |
| 601 } | |
| 602 } | |
| 603 } | |
| 604 }); | |
| 605 } | |
| 606 | |
| 607 @Override | |
| 608 public void onIceCandidate(final IceCandidate candidate) { | |
| 609 runOnUiThread(new Runnable() { | |
| 610 @Override | |
| 611 public void run() { | |
| 612 if (appRtcClient != null) { | |
| 613 appRtcClient.sendLocalIceCandidate(candidate); | |
| 614 } | |
| 615 } | |
| 616 }); | |
| 617 } | |
| 618 | |
| 619 @Override | |
| 620 public void onIceConnected() { | |
| 621 final long delta = System.currentTimeMillis() - callStartedTimeMs; | |
| 622 runOnUiThread(new Runnable() { | |
| 623 @Override | |
| 624 public void run() { | |
| 625 logAndToast("ICE connected, delay=" + delta + "ms"); | |
| 626 iceConnected = true; | |
| 627 callConnected(); | |
| 628 } | |
| 629 }); | |
| 630 } | |
| 631 | |
| 632 @Override | |
| 633 public void onIceDisconnected() { | |
| 634 runOnUiThread(new Runnable() { | |
| 635 @Override | |
| 636 public void run() { | |
| 637 logAndToast("ICE disconnected"); | |
| 638 iceConnected = false; | |
| 639 disconnect(); | |
| 640 } | |
| 641 }); | |
| 642 } | |
| 643 | |
| 644 @Override | |
| 645 public void onPeerConnectionClosed() { | |
| 646 } | |
| 647 | |
| 648 @Override | |
| 649 public void onPeerConnectionStatsReady(final StatsReport[] reports) { | |
| 650 runOnUiThread(new Runnable() { | |
| 651 @Override | |
| 652 public void run() { | |
| 653 if (!isError && iceConnected) { | |
| 654 hudFragment.updateEncoderStatistics(reports); | |
| 655 } | |
| 656 } | |
| 657 }); | |
| 658 } | |
| 659 | |
| 660 @Override | |
| 661 public void onPeerConnectionError(final String description) { | |
| 662 reportError(description); | |
| 663 } | |
| 664 } | |
| OLD | NEW |