1 /* 2 * Copyright 2013 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 package org.webrtc; 12 13 import static com.google.common.truth.Truth.assertThat; 14 import static org.junit.Assert.assertEquals; 15 import static org.junit.Assert.assertFalse; 16 import static org.junit.Assert.assertNotNull; 17 import static org.junit.Assert.assertNull; 18 import static org.junit.Assert.assertTrue; 19 20 import android.support.annotation.Nullable; 21 import android.support.test.InstrumentationRegistry; 22 import android.support.test.filters.MediumTest; 23 import android.support.test.filters.SmallTest; 24 import java.lang.ref.WeakReference; 25 import java.nio.ByteBuffer; 26 import java.nio.charset.Charset; 27 import java.util.ArrayDeque; 28 import java.util.ArrayList; 29 import java.util.Arrays; 30 import java.util.HashSet; 31 import java.util.IdentityHashMap; 32 import java.util.List; 33 import java.util.Map; 34 import java.util.Queue; 35 import java.util.TreeSet; 36 import java.util.concurrent.CountDownLatch; 37 import java.util.concurrent.TimeUnit; 38 import org.chromium.base.test.BaseJUnit4ClassRunner; 39 import org.junit.Before; 40 import org.junit.Test; 41 import org.junit.runner.RunWith; 42 import org.webrtc.PeerConnection.IceConnectionState; 43 import org.webrtc.PeerConnection.IceGatheringState; 44 import org.webrtc.PeerConnection.PeerConnectionState; 45 import org.webrtc.PeerConnection.SignalingState; 46 47 /** End-to-end tests for {@link PeerConnection}. */ 48 @RunWith(BaseJUnit4ClassRunner.class) 49 public class PeerConnectionEndToEndTest { 50 private static final String TAG = "PeerConnectionEndToEndTest"; 51 private static final int DEFAULT_TIMEOUT_SECONDS = 20; 52 private static final int SHORT_TIMEOUT_SECONDS = 5; 53 54 @Before setUp()55 public void setUp() { 56 PeerConnectionFactory.initialize(PeerConnectionFactory.InitializationOptions 57 .builder(InstrumentationRegistry.getTargetContext()) 58 .setNativeLibraryName(TestConstants.NATIVE_LIBRARY) 59 .createInitializationOptions()); 60 } 61 62 private static class ObserverExpectations 63 implements PeerConnection.Observer, VideoSink, DataChannel.Observer, StatsObserver, 64 RTCStatsCollectorCallback, RtpReceiver.Observer { 65 private final String name; 66 private int expectedIceCandidates; 67 private int expectedErrors; 68 private int expectedRenegotiations; 69 private int expectedWidth; 70 private int expectedHeight; 71 private int expectedFramesDelivered; 72 private int expectedTracksAdded; 73 private Queue<SignalingState> expectedSignalingChanges = new ArrayDeque<>(); 74 private Queue<IceConnectionState> expectedIceConnectionChanges = new ArrayDeque<>(); 75 private Queue<IceConnectionState> expectedStandardizedIceConnectionChanges = new ArrayDeque<>(); 76 private Queue<PeerConnectionState> expectedConnectionChanges = new ArrayDeque<>(); 77 private Queue<IceGatheringState> expectedIceGatheringChanges = new ArrayDeque<>(); 78 private Queue<String> expectedAddStreamLabels = new ArrayDeque<>(); 79 private Queue<String> expectedRemoveStreamLabels = new ArrayDeque<>(); 80 private final List<IceCandidate> gotIceCandidates = new ArrayList<>(); 81 private Map<MediaStream, WeakReference<VideoSink>> videoSinks = new IdentityHashMap<>(); 82 private DataChannel dataChannel; 83 private Queue<DataChannel.Buffer> expectedBuffers = new ArrayDeque<>(); 84 private Queue<DataChannel.State> expectedStateChanges = new ArrayDeque<>(); 85 private Queue<String> expectedRemoteDataChannelLabels = new ArrayDeque<>(); 86 private int expectedOldStatsCallbacks; 87 private int expectedNewStatsCallbacks; 88 private List<StatsReport[]> gotStatsReports = new ArrayList<>(); 89 private final HashSet<MediaStream> gotRemoteStreams = new HashSet<>(); 90 private int expectedFirstAudioPacket; 91 private int expectedFirstVideoPacket; 92 ObserverExpectations(String name)93 public ObserverExpectations(String name) { 94 this.name = name; 95 } 96 97 // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression. 98 @SuppressWarnings("NoSynchronizedMethodCheck") setDataChannel(DataChannel dataChannel)99 public synchronized void setDataChannel(DataChannel dataChannel) { 100 assertNull(this.dataChannel); 101 this.dataChannel = dataChannel; 102 this.dataChannel.registerObserver(this); 103 assertNotNull(this.dataChannel); 104 } 105 106 // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression. 107 @SuppressWarnings("NoSynchronizedMethodCheck") expectIceCandidates(int count)108 public synchronized void expectIceCandidates(int count) { 109 expectedIceCandidates += count; 110 } 111 112 @Override 113 // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression. 114 @SuppressWarnings("NoSynchronizedMethodCheck") onIceCandidate(IceCandidate candidate)115 public synchronized void onIceCandidate(IceCandidate candidate) { 116 Logging.d(TAG, "onIceCandidate: " + candidate.toString()); 117 --expectedIceCandidates; 118 119 // We don't assert expectedIceCandidates >= 0 because it's hard to know 120 // how many to expect, in general. We only use expectIceCandidates to 121 // assert a minimal count. 122 synchronized (gotIceCandidates) { 123 gotIceCandidates.add(candidate); 124 gotIceCandidates.notifyAll(); 125 } 126 } 127 128 @Override onIceCandidatesRemoved(IceCandidate[] candidates)129 public void onIceCandidatesRemoved(IceCandidate[] candidates) {} 130 131 @Override onSelectedCandidatePairChanged(CandidatePairChangeEvent event)132 public void onSelectedCandidatePairChanged(CandidatePairChangeEvent event) {} 133 134 // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression. 135 @SuppressWarnings("NoSynchronizedMethodCheck") setExpectedResolution(int width, int height)136 public synchronized void setExpectedResolution(int width, int height) { 137 expectedWidth = width; 138 expectedHeight = height; 139 } 140 141 // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression. 142 @SuppressWarnings("NoSynchronizedMethodCheck") expectFramesDelivered(int count)143 public synchronized void expectFramesDelivered(int count) { 144 expectedFramesDelivered += count; 145 } 146 147 @Override 148 // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression. 149 @SuppressWarnings("NoSynchronizedMethodCheck") onFrame(VideoFrame frame)150 public synchronized void onFrame(VideoFrame frame) { 151 if (expectedFramesDelivered <= 0) { 152 return; 153 } 154 assertTrue(expectedWidth > 0); 155 assertTrue(expectedHeight > 0); 156 assertEquals(expectedWidth, frame.getRotatedWidth()); 157 assertEquals(expectedHeight, frame.getRotatedHeight()); 158 --expectedFramesDelivered; 159 } 160 161 // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression. 162 @SuppressWarnings("NoSynchronizedMethodCheck") expectSignalingChange(SignalingState newState)163 public synchronized void expectSignalingChange(SignalingState newState) { 164 expectedSignalingChanges.add(newState); 165 } 166 167 @Override 168 // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression. 169 @SuppressWarnings("NoSynchronizedMethodCheck") onSignalingChange(SignalingState newState)170 public synchronized void onSignalingChange(SignalingState newState) { 171 assertEquals(expectedSignalingChanges.remove(), newState); 172 } 173 174 // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression. 175 @SuppressWarnings("NoSynchronizedMethodCheck") expectIceConnectionChange(IceConnectionState newState)176 public synchronized void expectIceConnectionChange(IceConnectionState newState) { 177 expectedIceConnectionChanges.add(newState); 178 } 179 180 // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression. 181 @SuppressWarnings("NoSynchronizedMethodCheck") expectStandardizedIceConnectionChange(IceConnectionState newState)182 public synchronized void expectStandardizedIceConnectionChange(IceConnectionState newState) { 183 expectedStandardizedIceConnectionChanges.add(newState); 184 } 185 186 @Override 187 // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression. 188 @SuppressWarnings("NoSynchronizedMethodCheck") onIceConnectionChange(IceConnectionState newState)189 public synchronized void onIceConnectionChange(IceConnectionState newState) { 190 // TODO(bemasc): remove once delivery of ICECompleted is reliable 191 // (https://code.google.com/p/webrtc/issues/detail?id=3021). 192 if (newState.equals(IceConnectionState.COMPLETED)) { 193 return; 194 } 195 196 if (expectedIceConnectionChanges.isEmpty()) { 197 Logging.d(TAG, name + "Got an unexpected ICE connection change " + newState); 198 return; 199 } 200 201 assertEquals(expectedIceConnectionChanges.remove(), newState); 202 } 203 204 @Override 205 // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression. 206 @SuppressWarnings("NoSynchronizedMethodCheck") onStandardizedIceConnectionChange(IceConnectionState newState)207 public synchronized void onStandardizedIceConnectionChange(IceConnectionState newState) { 208 if (newState.equals(IceConnectionState.COMPLETED)) { 209 return; 210 } 211 212 if (expectedIceConnectionChanges.isEmpty()) { 213 Logging.d(TAG, name + "Got an unexpected standardized ICE connection change " + newState); 214 return; 215 } 216 217 assertEquals(expectedStandardizedIceConnectionChanges.remove(), newState); 218 } 219 220 // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression. 221 @SuppressWarnings("NoSynchronizedMethodCheck") expectConnectionChange(PeerConnectionState newState)222 public synchronized void expectConnectionChange(PeerConnectionState newState) { 223 expectedConnectionChanges.add(newState); 224 } 225 226 @Override 227 // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression. 228 @SuppressWarnings("NoSynchronizedMethodCheck") onConnectionChange(PeerConnectionState newState)229 public synchronized void onConnectionChange(PeerConnectionState newState) { 230 if (expectedConnectionChanges.isEmpty()) { 231 Logging.d(TAG, name + " got an unexpected DTLS connection change " + newState); 232 return; 233 } 234 235 assertEquals(expectedConnectionChanges.remove(), newState); 236 } 237 238 @Override 239 // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression. 240 @SuppressWarnings("NoSynchronizedMethodCheck") onIceConnectionReceivingChange(boolean receiving)241 public synchronized void onIceConnectionReceivingChange(boolean receiving) { 242 Logging.d(TAG, name + " got an ICE connection receiving change " + receiving); 243 } 244 245 // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression. 246 @SuppressWarnings("NoSynchronizedMethodCheck") expectIceGatheringChange(IceGatheringState newState)247 public synchronized void expectIceGatheringChange(IceGatheringState newState) { 248 expectedIceGatheringChanges.add(newState); 249 } 250 251 @Override 252 // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression. 253 @SuppressWarnings("NoSynchronizedMethodCheck") onIceGatheringChange(IceGatheringState newState)254 public synchronized void onIceGatheringChange(IceGatheringState newState) { 255 // It's fine to get a variable number of GATHERING messages before 256 // COMPLETE fires (depending on how long the test runs) so we don't assert 257 // any particular count. 258 if (newState == IceGatheringState.GATHERING) { 259 return; 260 } 261 if (expectedIceGatheringChanges.isEmpty()) { 262 Logging.d(TAG, name + "Got an unexpected ICE gathering change " + newState); 263 } 264 assertEquals(expectedIceGatheringChanges.remove(), newState); 265 } 266 267 // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression. 268 @SuppressWarnings("NoSynchronizedMethodCheck") expectAddStream(String label)269 public synchronized void expectAddStream(String label) { 270 expectedAddStreamLabels.add(label); 271 } 272 273 @Override 274 // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression. 275 @SuppressWarnings("NoSynchronizedMethodCheck") onAddStream(MediaStream stream)276 public synchronized void onAddStream(MediaStream stream) { 277 assertEquals(expectedAddStreamLabels.remove(), stream.getId()); 278 for (AudioTrack track : stream.audioTracks) { 279 assertEquals("audio", track.kind()); 280 } 281 for (VideoTrack track : stream.videoTracks) { 282 assertEquals("video", track.kind()); 283 track.addSink(this); 284 assertNull(videoSinks.put(stream, new WeakReference<VideoSink>(this))); 285 } 286 gotRemoteStreams.add(stream); 287 } 288 289 // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression. 290 @SuppressWarnings("NoSynchronizedMethodCheck") expectRemoveStream(String label)291 public synchronized void expectRemoveStream(String label) { 292 expectedRemoveStreamLabels.add(label); 293 } 294 295 @Override 296 // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression. 297 @SuppressWarnings("NoSynchronizedMethodCheck") onRemoveStream(MediaStream stream)298 public synchronized void onRemoveStream(MediaStream stream) { 299 assertEquals(expectedRemoveStreamLabels.remove(), stream.getId()); 300 WeakReference<VideoSink> videoSink = videoSinks.remove(stream); 301 assertNotNull(videoSink); 302 assertNotNull(videoSink.get()); 303 for (VideoTrack videoTrack : stream.videoTracks) { 304 videoTrack.removeSink(videoSink.get()); 305 } 306 gotRemoteStreams.remove(stream); 307 } 308 309 // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression. 310 @SuppressWarnings("NoSynchronizedMethodCheck") expectDataChannel(String label)311 public synchronized void expectDataChannel(String label) { 312 expectedRemoteDataChannelLabels.add(label); 313 } 314 315 @Override 316 // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression. 317 @SuppressWarnings("NoSynchronizedMethodCheck") onDataChannel(DataChannel remoteDataChannel)318 public synchronized void onDataChannel(DataChannel remoteDataChannel) { 319 assertEquals(expectedRemoteDataChannelLabels.remove(), remoteDataChannel.label()); 320 setDataChannel(remoteDataChannel); 321 assertEquals(DataChannel.State.CONNECTING, dataChannel.state()); 322 } 323 324 // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression. 325 @SuppressWarnings("NoSynchronizedMethodCheck") expectRenegotiationNeeded()326 public synchronized void expectRenegotiationNeeded() { 327 ++expectedRenegotiations; 328 } 329 330 @Override 331 // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression. 332 @SuppressWarnings("NoSynchronizedMethodCheck") onRenegotiationNeeded()333 public synchronized void onRenegotiationNeeded() { 334 assertTrue(--expectedRenegotiations >= 0); 335 } 336 337 // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression. 338 @SuppressWarnings("NoSynchronizedMethodCheck") expectAddTrack(int expectedTracksAdded)339 public synchronized void expectAddTrack(int expectedTracksAdded) { 340 this.expectedTracksAdded = expectedTracksAdded; 341 } 342 343 @Override 344 // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression. 345 @SuppressWarnings("NoSynchronizedMethodCheck") onAddTrack(RtpReceiver receiver, MediaStream[] mediaStreams)346 public synchronized void onAddTrack(RtpReceiver receiver, MediaStream[] mediaStreams) { 347 expectedTracksAdded--; 348 } 349 350 // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression. 351 @SuppressWarnings("NoSynchronizedMethodCheck") expectMessage(ByteBuffer expectedBuffer, boolean expectedBinary)352 public synchronized void expectMessage(ByteBuffer expectedBuffer, boolean expectedBinary) { 353 expectedBuffers.add(new DataChannel.Buffer(expectedBuffer, expectedBinary)); 354 } 355 356 @Override 357 // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression. 358 @SuppressWarnings("NoSynchronizedMethodCheck") onMessage(DataChannel.Buffer buffer)359 public synchronized void onMessage(DataChannel.Buffer buffer) { 360 DataChannel.Buffer expected = expectedBuffers.remove(); 361 assertEquals(expected.binary, buffer.binary); 362 assertTrue(expected.data.equals(buffer.data)); 363 } 364 365 @Override 366 // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression. 367 @SuppressWarnings("NoSynchronizedMethodCheck") onBufferedAmountChange(long previousAmount)368 public synchronized void onBufferedAmountChange(long previousAmount) { 369 assertFalse(previousAmount == dataChannel.bufferedAmount()); 370 } 371 372 @Override 373 // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression. 374 @SuppressWarnings("NoSynchronizedMethodCheck") onStateChange()375 public synchronized void onStateChange() { 376 assertEquals(expectedStateChanges.remove(), dataChannel.state()); 377 } 378 379 // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression. 380 @SuppressWarnings("NoSynchronizedMethodCheck") expectStateChange(DataChannel.State state)381 public synchronized void expectStateChange(DataChannel.State state) { 382 expectedStateChanges.add(state); 383 } 384 385 // Old getStats callback. 386 @Override 387 // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression. 388 @SuppressWarnings("NoSynchronizedMethodCheck") onComplete(StatsReport[] reports)389 public synchronized void onComplete(StatsReport[] reports) { 390 if (--expectedOldStatsCallbacks < 0) { 391 throw new RuntimeException("Unexpected stats report: " + Arrays.toString(reports)); 392 } 393 gotStatsReports.add(reports); 394 } 395 396 // New getStats callback. 397 @Override 398 // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression. 399 @SuppressWarnings("NoSynchronizedMethodCheck") onStatsDelivered(RTCStatsReport report)400 public synchronized void onStatsDelivered(RTCStatsReport report) { 401 if (--expectedNewStatsCallbacks < 0) { 402 throw new RuntimeException("Unexpected stats report: " + report); 403 } 404 } 405 406 @Override 407 // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression. 408 @SuppressWarnings("NoSynchronizedMethodCheck") onFirstPacketReceived(MediaStreamTrack.MediaType mediaType)409 public synchronized void onFirstPacketReceived(MediaStreamTrack.MediaType mediaType) { 410 if (mediaType == MediaStreamTrack.MediaType.MEDIA_TYPE_AUDIO) { 411 expectedFirstAudioPacket--; 412 } else { 413 expectedFirstVideoPacket--; 414 } 415 if (expectedFirstAudioPacket < 0 || expectedFirstVideoPacket < 0) { 416 throw new RuntimeException("Unexpected call of onFirstPacketReceived"); 417 } 418 } 419 420 // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression. 421 @SuppressWarnings("NoSynchronizedMethodCheck") expectFirstPacketReceived()422 public synchronized void expectFirstPacketReceived() { 423 expectedFirstAudioPacket = 1; 424 expectedFirstVideoPacket = 1; 425 } 426 427 // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression. 428 @SuppressWarnings("NoSynchronizedMethodCheck") expectOldStatsCallback()429 public synchronized void expectOldStatsCallback() { 430 ++expectedOldStatsCallbacks; 431 } 432 433 // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression. 434 @SuppressWarnings("NoSynchronizedMethodCheck") expectNewStatsCallback()435 public synchronized void expectNewStatsCallback() { 436 ++expectedNewStatsCallbacks; 437 } 438 439 // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression. 440 @SuppressWarnings("NoSynchronizedMethodCheck") takeStatsReports()441 public synchronized List<StatsReport[]> takeStatsReports() { 442 List<StatsReport[]> got = gotStatsReports; 443 gotStatsReports = new ArrayList<StatsReport[]>(); 444 return got; 445 } 446 447 // Return a set of expectations that haven't been satisfied yet, possibly 448 // empty if no such expectations exist. 449 // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression. 450 @SuppressWarnings("NoSynchronizedMethodCheck") unsatisfiedExpectations()451 public synchronized TreeSet<String> unsatisfiedExpectations() { 452 TreeSet<String> stillWaitingForExpectations = new TreeSet<String>(); 453 if (expectedIceCandidates > 0) { // See comment in onIceCandidate. 454 stillWaitingForExpectations.add("expectedIceCandidates"); 455 } 456 if (expectedErrors != 0) { 457 stillWaitingForExpectations.add("expectedErrors: " + expectedErrors); 458 } 459 if (expectedSignalingChanges.size() != 0) { 460 stillWaitingForExpectations.add( 461 "expectedSignalingChanges: " + expectedSignalingChanges.size()); 462 } 463 if (expectedIceConnectionChanges.size() != 0) { 464 stillWaitingForExpectations.add( 465 "expectedIceConnectionChanges: " + expectedIceConnectionChanges.size()); 466 } 467 if (expectedIceGatheringChanges.size() != 0) { 468 stillWaitingForExpectations.add( 469 "expectedIceGatheringChanges: " + expectedIceGatheringChanges.size()); 470 } 471 if (expectedAddStreamLabels.size() != 0) { 472 stillWaitingForExpectations.add( 473 "expectedAddStreamLabels: " + expectedAddStreamLabels.size()); 474 } 475 if (expectedRemoveStreamLabels.size() != 0) { 476 stillWaitingForExpectations.add( 477 "expectedRemoveStreamLabels: " + expectedRemoveStreamLabels.size()); 478 } 479 if (expectedFramesDelivered > 0) { 480 stillWaitingForExpectations.add("expectedFramesDelivered: " + expectedFramesDelivered); 481 } 482 if (!expectedBuffers.isEmpty()) { 483 stillWaitingForExpectations.add("expectedBuffers: " + expectedBuffers.size()); 484 } 485 if (!expectedStateChanges.isEmpty()) { 486 stillWaitingForExpectations.add("expectedStateChanges: " + expectedStateChanges.size()); 487 } 488 if (!expectedRemoteDataChannelLabels.isEmpty()) { 489 stillWaitingForExpectations.add( 490 "expectedRemoteDataChannelLabels: " + expectedRemoteDataChannelLabels.size()); 491 } 492 if (expectedOldStatsCallbacks != 0) { 493 stillWaitingForExpectations.add("expectedOldStatsCallbacks: " + expectedOldStatsCallbacks); 494 } 495 if (expectedNewStatsCallbacks != 0) { 496 stillWaitingForExpectations.add("expectedNewStatsCallbacks: " + expectedNewStatsCallbacks); 497 } 498 if (expectedFirstAudioPacket > 0) { 499 stillWaitingForExpectations.add("expectedFirstAudioPacket: " + expectedFirstAudioPacket); 500 } 501 if (expectedFirstVideoPacket > 0) { 502 stillWaitingForExpectations.add("expectedFirstVideoPacket: " + expectedFirstVideoPacket); 503 } 504 if (expectedTracksAdded != 0) { 505 stillWaitingForExpectations.add("expectedAddedTrack: " + expectedTracksAdded); 506 } 507 return stillWaitingForExpectations; 508 } 509 waitForAllExpectationsToBeSatisfied(int timeoutSeconds)510 public boolean waitForAllExpectationsToBeSatisfied(int timeoutSeconds) { 511 // TODO(fischman): problems with this approach: 512 // - come up with something better than a poll loop 513 // - avoid serializing expectations explicitly; the test is not as robust 514 // as it could be because it must place expectations between wait 515 // statements very precisely (e.g. frame must not arrive before its 516 // expectation, and expectation must not be registered so early as to 517 // stall a wait). Use callbacks to fire off dependent steps instead of 518 // explicitly waiting, so there can be just a single wait at the end of 519 // the test. 520 long endTime = System.currentTimeMillis() + 1000 * timeoutSeconds; 521 TreeSet<String> prev = null; 522 TreeSet<String> stillWaitingForExpectations = unsatisfiedExpectations(); 523 while (!stillWaitingForExpectations.isEmpty()) { 524 if (!stillWaitingForExpectations.equals(prev)) { 525 Logging.d(TAG, 526 name + " still waiting at\n " + (new Throwable()).getStackTrace()[1] 527 + "\n for: " + Arrays.toString(stillWaitingForExpectations.toArray())); 528 } 529 if (endTime < System.currentTimeMillis()) { 530 Logging.d(TAG, 531 name + " timed out waiting for: " 532 + Arrays.toString(stillWaitingForExpectations.toArray())); 533 return false; 534 } 535 try { 536 Thread.sleep(10); 537 } catch (InterruptedException e) { 538 throw new RuntimeException(e); 539 } 540 prev = stillWaitingForExpectations; 541 stillWaitingForExpectations = unsatisfiedExpectations(); 542 } 543 if (prev == null) { 544 Logging.d( 545 TAG, name + " didn't need to wait at\n " + (new Throwable()).getStackTrace()[1]); 546 } 547 return true; 548 } 549 550 // This methods return a list of all currently gathered ice candidates or waits until 551 // 1 candidate have been gathered. getAtLeastOneIceCandidate()552 public List<IceCandidate> getAtLeastOneIceCandidate() throws InterruptedException { 553 synchronized (gotIceCandidates) { 554 while (gotIceCandidates.isEmpty()) { 555 gotIceCandidates.wait(); 556 } 557 return new ArrayList<IceCandidate>(gotIceCandidates); 558 } 559 } 560 } 561 562 // Sets the expected resolution for an ObserverExpectations once a frame 563 // has been captured. 564 private static class ExpectedResolutionSetter implements VideoSink { 565 private ObserverExpectations observer; 566 ExpectedResolutionSetter(ObserverExpectations observer)567 public ExpectedResolutionSetter(ObserverExpectations observer) { 568 this.observer = observer; 569 } 570 571 @Override 572 // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression. 573 @SuppressWarnings("NoSynchronizedMethodCheck") onFrame(VideoFrame frame)574 public synchronized void onFrame(VideoFrame frame) { 575 // Because different camera devices (fake & physical) produce different 576 // resolutions, we only sanity-check the set sizes, 577 assertTrue(frame.getRotatedWidth() > 0); 578 assertTrue(frame.getRotatedHeight() > 0); 579 observer.setExpectedResolution(frame.getRotatedWidth(), frame.getRotatedHeight()); 580 frame.retain(); 581 } 582 } 583 584 private static class SdpObserverLatch implements SdpObserver { 585 private boolean success; 586 private @Nullable SessionDescription sdp; 587 private @Nullable String error; 588 private CountDownLatch latch = new CountDownLatch(1); 589 SdpObserverLatch()590 public SdpObserverLatch() {} 591 592 @Override onCreateSuccess(SessionDescription sdp)593 public void onCreateSuccess(SessionDescription sdp) { 594 this.sdp = sdp; 595 onSetSuccess(); 596 } 597 598 @Override onSetSuccess()599 public void onSetSuccess() { 600 success = true; 601 latch.countDown(); 602 } 603 604 @Override onCreateFailure(String error)605 public void onCreateFailure(String error) { 606 onSetFailure(error); 607 } 608 609 @Override onSetFailure(String error)610 public void onSetFailure(String error) { 611 this.error = error; 612 latch.countDown(); 613 } 614 await()615 public boolean await() { 616 try { 617 assertTrue(latch.await(1000, TimeUnit.MILLISECONDS)); 618 return getSuccess(); 619 } catch (Exception e) { 620 throw new RuntimeException(e); 621 } 622 } 623 getSuccess()624 public boolean getSuccess() { 625 return success; 626 } 627 getSdp()628 public @Nullable SessionDescription getSdp() { 629 return sdp; 630 } 631 getError()632 public @Nullable String getError() { 633 return error; 634 } 635 } 636 637 // Return a weak reference to test that ownership is correctly held by 638 // PeerConnection, not by test code. addTracksToPC(PeerConnectionFactory factory, PeerConnection pc, VideoSource videoSource, String streamLabel, String videoTrackId, String audioTrackId, VideoSink videoSink)639 private static WeakReference<MediaStream> addTracksToPC(PeerConnectionFactory factory, 640 PeerConnection pc, VideoSource videoSource, String streamLabel, String videoTrackId, 641 String audioTrackId, VideoSink videoSink) { 642 MediaStream lMS = factory.createLocalMediaStream(streamLabel); 643 VideoTrack videoTrack = factory.createVideoTrack(videoTrackId, videoSource); 644 assertNotNull(videoTrack); 645 assertNotNull(videoSink); 646 videoTrack.addSink(videoSink); 647 lMS.addTrack(videoTrack); 648 // Just for fun, let's remove and re-add the track. 649 lMS.removeTrack(videoTrack); 650 lMS.addTrack(videoTrack); 651 lMS.addTrack( 652 factory.createAudioTrack(audioTrackId, factory.createAudioSource(new MediaConstraints()))); 653 pc.addStream(lMS); 654 return new WeakReference<MediaStream>(lMS); 655 } 656 657 @Test 658 @MediumTest testCompleteSession()659 public void testCompleteSession() throws Exception { 660 Metrics.enable(); 661 // Allow loopback interfaces too since our Android devices often don't 662 // have those. 663 PeerConnectionFactory.Options options = new PeerConnectionFactory.Options(); 664 options.networkIgnoreMask = 0; 665 PeerConnectionFactory factory = PeerConnectionFactory.builder() 666 .setOptions(options) 667 .setVideoEncoderFactory(new SoftwareVideoEncoderFactory()) 668 .setVideoDecoderFactory(new SoftwareVideoDecoderFactory()) 669 .createPeerConnectionFactory(); 670 671 List<PeerConnection.IceServer> iceServers = new ArrayList<>(); 672 iceServers.add( 673 PeerConnection.IceServer.builder("stun:stun.l.google.com:19302").createIceServer()); 674 iceServers.add(PeerConnection.IceServer.builder("turn:fake.example.com") 675 .setUsername("fakeUsername") 676 .setPassword("fakePassword") 677 .createIceServer()); 678 679 PeerConnection.RTCConfiguration rtcConfig = new PeerConnection.RTCConfiguration(iceServers); 680 rtcConfig.enableDtlsSrtp = true; 681 682 ObserverExpectations offeringExpectations = new ObserverExpectations("PCTest:offerer"); 683 PeerConnection offeringPC = factory.createPeerConnection(rtcConfig, offeringExpectations); 684 assertNotNull(offeringPC); 685 686 ObserverExpectations answeringExpectations = new ObserverExpectations("PCTest:answerer"); 687 PeerConnection answeringPC = factory.createPeerConnection(rtcConfig, answeringExpectations); 688 assertNotNull(answeringPC); 689 690 // We want to use the same camera for offerer & answerer, so create it here 691 // instead of in addTracksToPC. 692 final CameraEnumerator enumerator = new Camera1Enumerator(false /* captureToTexture */); 693 final VideoCapturer videoCapturer = 694 enumerator.createCapturer(enumerator.getDeviceNames()[0], null /* eventsHandler */); 695 final SurfaceTextureHelper surfaceTextureHelper = 696 SurfaceTextureHelper.create("SurfaceTextureHelper", /* sharedContext= */ null); 697 final VideoSource videoSource = factory.createVideoSource(/* isScreencast= */ false); 698 videoCapturer.initialize(surfaceTextureHelper, InstrumentationRegistry.getTargetContext(), 699 videoSource.getCapturerObserver()); 700 videoCapturer.startCapture(640, 480, 30); 701 702 offeringExpectations.expectRenegotiationNeeded(); 703 WeakReference<MediaStream> oLMS = 704 addTracksToPC(factory, offeringPC, videoSource, "offeredMediaStream", "offeredVideoTrack", 705 "offeredAudioTrack", new ExpectedResolutionSetter(answeringExpectations)); 706 707 offeringExpectations.expectAddTrack(2); 708 answeringExpectations.expectAddTrack(2); 709 710 offeringExpectations.expectRenegotiationNeeded(); 711 DataChannel offeringDC = offeringPC.createDataChannel("offeringDC", new DataChannel.Init()); 712 assertEquals("offeringDC", offeringDC.label()); 713 714 offeringExpectations.setDataChannel(offeringDC); 715 SdpObserverLatch sdpLatch = new SdpObserverLatch(); 716 offeringPC.createOffer(sdpLatch, new MediaConstraints()); 717 assertTrue(sdpLatch.await()); 718 SessionDescription offerSdp = sdpLatch.getSdp(); 719 assertEquals(offerSdp.type, SessionDescription.Type.OFFER); 720 assertFalse(offerSdp.description.isEmpty()); 721 722 sdpLatch = new SdpObserverLatch(); 723 answeringExpectations.expectSignalingChange(SignalingState.HAVE_REMOTE_OFFER); 724 answeringExpectations.expectAddStream("offeredMediaStream"); 725 // SCTP DataChannels are announced via OPEN messages over the established 726 // connection (not via SDP), so answeringExpectations can only register 727 // expecting the channel during ICE, below. 728 answeringPC.setRemoteDescription(sdpLatch, offerSdp); 729 assertEquals(PeerConnection.SignalingState.STABLE, offeringPC.signalingState()); 730 assertTrue(sdpLatch.await()); 731 assertNull(sdpLatch.getSdp()); 732 733 answeringExpectations.expectRenegotiationNeeded(); 734 WeakReference<MediaStream> aLMS = addTracksToPC(factory, answeringPC, videoSource, 735 "answeredMediaStream", "answeredVideoTrack", "answeredAudioTrack", 736 new ExpectedResolutionSetter(offeringExpectations)); 737 738 sdpLatch = new SdpObserverLatch(); 739 answeringPC.createAnswer(sdpLatch, new MediaConstraints()); 740 assertTrue(sdpLatch.await()); 741 SessionDescription answerSdp = sdpLatch.getSdp(); 742 assertEquals(answerSdp.type, SessionDescription.Type.ANSWER); 743 assertFalse(answerSdp.description.isEmpty()); 744 745 offeringExpectations.expectIceCandidates(2); 746 answeringExpectations.expectIceCandidates(2); 747 748 offeringExpectations.expectIceGatheringChange(IceGatheringState.COMPLETE); 749 answeringExpectations.expectIceGatheringChange(IceGatheringState.COMPLETE); 750 751 sdpLatch = new SdpObserverLatch(); 752 answeringExpectations.expectSignalingChange(SignalingState.STABLE); 753 answeringExpectations.expectConnectionChange(PeerConnectionState.CONNECTING); 754 answeringPC.setLocalDescription(sdpLatch, answerSdp); 755 assertTrue(sdpLatch.await()); 756 assertNull(sdpLatch.getSdp()); 757 758 sdpLatch = new SdpObserverLatch(); 759 offeringExpectations.expectSignalingChange(SignalingState.HAVE_LOCAL_OFFER); 760 offeringExpectations.expectConnectionChange(PeerConnectionState.CONNECTING); 761 offeringPC.setLocalDescription(sdpLatch, offerSdp); 762 assertTrue(sdpLatch.await()); 763 assertNull(sdpLatch.getSdp()); 764 sdpLatch = new SdpObserverLatch(); 765 offeringExpectations.expectSignalingChange(SignalingState.STABLE); 766 offeringExpectations.expectAddStream("answeredMediaStream"); 767 768 offeringExpectations.expectIceConnectionChange(IceConnectionState.CHECKING); 769 offeringExpectations.expectIceConnectionChange(IceConnectionState.CONNECTED); 770 offeringExpectations.expectStandardizedIceConnectionChange(IceConnectionState.CHECKING); 771 offeringExpectations.expectStandardizedIceConnectionChange(IceConnectionState.CONNECTED); 772 offeringExpectations.expectConnectionChange(PeerConnectionState.CONNECTED); 773 // TODO(bemasc): uncomment once delivery of ICECompleted is reliable 774 // (https://code.google.com/p/webrtc/issues/detail?id=3021). 775 // 776 // offeringExpectations.expectIceConnectionChange( 777 // IceConnectionState.COMPLETED); 778 answeringExpectations.expectIceConnectionChange(IceConnectionState.CHECKING); 779 answeringExpectations.expectIceConnectionChange(IceConnectionState.CONNECTED); 780 answeringExpectations.expectStandardizedIceConnectionChange(IceConnectionState.CHECKING); 781 answeringExpectations.expectStandardizedIceConnectionChange(IceConnectionState.CONNECTED); 782 answeringExpectations.expectConnectionChange(PeerConnectionState.CONNECTED); 783 784 offeringPC.setRemoteDescription(sdpLatch, answerSdp); 785 assertTrue(sdpLatch.await()); 786 assertNull(sdpLatch.getSdp()); 787 788 assertEquals(offeringPC.getLocalDescription().type, offerSdp.type); 789 assertEquals(offeringPC.getRemoteDescription().type, answerSdp.type); 790 assertEquals(answeringPC.getLocalDescription().type, answerSdp.type); 791 assertEquals(answeringPC.getRemoteDescription().type, offerSdp.type); 792 793 assertEquals(offeringPC.getSenders().size(), 2); 794 assertEquals(offeringPC.getReceivers().size(), 2); 795 assertEquals(answeringPC.getSenders().size(), 2); 796 assertEquals(answeringPC.getReceivers().size(), 2); 797 798 offeringExpectations.expectFirstPacketReceived(); 799 answeringExpectations.expectFirstPacketReceived(); 800 801 for (RtpReceiver receiver : offeringPC.getReceivers()) { 802 receiver.SetObserver(offeringExpectations); 803 } 804 805 for (RtpReceiver receiver : answeringPC.getReceivers()) { 806 receiver.SetObserver(answeringExpectations); 807 } 808 809 // Wait for at least some frames to be delivered at each end (number 810 // chosen arbitrarily). 811 offeringExpectations.expectFramesDelivered(10); 812 answeringExpectations.expectFramesDelivered(10); 813 814 offeringExpectations.expectStateChange(DataChannel.State.OPEN); 815 // See commentary about SCTP DataChannels above for why this is here. 816 answeringExpectations.expectDataChannel("offeringDC"); 817 answeringExpectations.expectStateChange(DataChannel.State.OPEN); 818 819 // Wait for at least one ice candidate from the offering PC and forward them to the answering 820 // PC. 821 for (IceCandidate candidate : offeringExpectations.getAtLeastOneIceCandidate()) { 822 answeringPC.addIceCandidate(candidate); 823 } 824 825 // Wait for at least one ice candidate from the answering PC and forward them to the offering 826 // PC. 827 for (IceCandidate candidate : answeringExpectations.getAtLeastOneIceCandidate()) { 828 offeringPC.addIceCandidate(candidate); 829 } 830 831 assertTrue(offeringExpectations.waitForAllExpectationsToBeSatisfied(DEFAULT_TIMEOUT_SECONDS)); 832 assertTrue(answeringExpectations.waitForAllExpectationsToBeSatisfied(DEFAULT_TIMEOUT_SECONDS)); 833 834 assertEquals(PeerConnection.SignalingState.STABLE, offeringPC.signalingState()); 835 assertEquals(PeerConnection.SignalingState.STABLE, answeringPC.signalingState()); 836 837 // Test some of the RtpSender API. 838 RtpSender videoSender = null; 839 RtpSender audioSender = null; 840 for (RtpSender sender : offeringPC.getSenders()) { 841 if (sender.track().kind().equals("video")) { 842 videoSender = sender; 843 } else { 844 audioSender = sender; 845 } 846 } 847 assertNotNull(videoSender); 848 assertNotNull(audioSender); 849 850 // Set a bitrate limit for the outgoing video stream for the offerer. 851 RtpParameters rtpParameters = videoSender.getParameters(); 852 assertNotNull(rtpParameters); 853 assertEquals(1, rtpParameters.encodings.size()); 854 assertNull(rtpParameters.encodings.get(0).maxBitrateBps); 855 assertNull(rtpParameters.encodings.get(0).minBitrateBps); 856 assertNull(rtpParameters.encodings.get(0).maxFramerate); 857 assertNull(rtpParameters.encodings.get(0).numTemporalLayers); 858 assertNull(rtpParameters.encodings.get(0).scaleResolutionDownBy); 859 assertTrue(rtpParameters.encodings.get(0).rid.isEmpty()); 860 861 rtpParameters.encodings.get(0).maxBitrateBps = 300000; 862 rtpParameters.encodings.get(0).minBitrateBps = 100000; 863 rtpParameters.encodings.get(0).maxFramerate = 20; 864 rtpParameters.encodings.get(0).numTemporalLayers = 2; 865 rtpParameters.encodings.get(0).scaleResolutionDownBy = 2.0; 866 assertTrue(videoSender.setParameters(rtpParameters)); 867 868 // Create a DTMF sender. 869 DtmfSender dtmfSender = audioSender.dtmf(); 870 assertNotNull(dtmfSender); 871 assertTrue(dtmfSender.canInsertDtmf()); 872 assertTrue(dtmfSender.insertDtmf("123", 300, 100)); 873 874 // Verify that we can read back the updated value. 875 rtpParameters = videoSender.getParameters(); 876 assertEquals(300000, (int) rtpParameters.encodings.get(0).maxBitrateBps); 877 assertEquals(100000, (int) rtpParameters.encodings.get(0).minBitrateBps); 878 assertEquals(20, (int) rtpParameters.encodings.get(0).maxFramerate); 879 assertEquals(2, (int) rtpParameters.encodings.get(0).numTemporalLayers); 880 assertThat(rtpParameters.encodings.get(0).scaleResolutionDownBy).isEqualTo(2.0); 881 882 // Test send & receive UTF-8 text. 883 answeringExpectations.expectMessage( 884 ByteBuffer.wrap("hello!".getBytes(Charset.forName("UTF-8"))), false); 885 DataChannel.Buffer buffer = 886 new DataChannel.Buffer(ByteBuffer.wrap("hello!".getBytes(Charset.forName("UTF-8"))), false); 887 assertTrue(offeringExpectations.dataChannel.send(buffer)); 888 assertTrue(answeringExpectations.waitForAllExpectationsToBeSatisfied(DEFAULT_TIMEOUT_SECONDS)); 889 890 // Construct this binary message two different ways to ensure no 891 // shortcuts are taken. 892 ByteBuffer expectedBinaryMessage = ByteBuffer.allocateDirect(5); 893 for (byte i = 1; i < 6; ++i) { 894 expectedBinaryMessage.put(i); 895 } 896 expectedBinaryMessage.flip(); 897 offeringExpectations.expectMessage(expectedBinaryMessage, true); 898 assertTrue(answeringExpectations.dataChannel.send( 899 new DataChannel.Buffer(ByteBuffer.wrap(new byte[] {1, 2, 3, 4, 5}), true))); 900 assertTrue(offeringExpectations.waitForAllExpectationsToBeSatisfied(DEFAULT_TIMEOUT_SECONDS)); 901 902 offeringExpectations.expectStateChange(DataChannel.State.CLOSING); 903 answeringExpectations.expectStateChange(DataChannel.State.CLOSING); 904 offeringExpectations.expectStateChange(DataChannel.State.CLOSED); 905 answeringExpectations.expectStateChange(DataChannel.State.CLOSED); 906 answeringExpectations.dataChannel.close(); 907 offeringExpectations.dataChannel.close(); 908 assertTrue(offeringExpectations.waitForAllExpectationsToBeSatisfied(DEFAULT_TIMEOUT_SECONDS)); 909 assertTrue(answeringExpectations.waitForAllExpectationsToBeSatisfied(DEFAULT_TIMEOUT_SECONDS)); 910 911 // Test SetBitrate. 912 assertTrue(offeringPC.setBitrate(100000, 5000000, 500000000)); 913 assertFalse(offeringPC.setBitrate(3, 2, 1)); 914 915 // Free the Java-land objects and collect them. 916 shutdownPC(offeringPC, offeringExpectations); 917 offeringPC = null; 918 shutdownPC(answeringPC, answeringExpectations); 919 answeringPC = null; 920 videoCapturer.stopCapture(); 921 videoCapturer.dispose(); 922 videoSource.dispose(); 923 surfaceTextureHelper.dispose(); 924 factory.dispose(); 925 System.gc(); 926 } 927 928 @Test 929 @MediumTest testDataChannelOnlySession()930 public void testDataChannelOnlySession() throws Exception { 931 // Allow loopback interfaces too since our Android devices often don't 932 // have those. 933 PeerConnectionFactory.Options options = new PeerConnectionFactory.Options(); 934 options.networkIgnoreMask = 0; 935 PeerConnectionFactory factory = 936 PeerConnectionFactory.builder().setOptions(options).createPeerConnectionFactory(); 937 938 List<PeerConnection.IceServer> iceServers = new ArrayList<>(); 939 iceServers.add( 940 PeerConnection.IceServer.builder("stun:stun.l.google.com:19302").createIceServer()); 941 iceServers.add(PeerConnection.IceServer.builder("turn:fake.example.com") 942 .setUsername("fakeUsername") 943 .setPassword("fakePassword") 944 .createIceServer()); 945 946 PeerConnection.RTCConfiguration rtcConfig = new PeerConnection.RTCConfiguration(iceServers); 947 rtcConfig.enableDtlsSrtp = true; 948 949 ObserverExpectations offeringExpectations = new ObserverExpectations("PCTest:offerer"); 950 PeerConnection offeringPC = factory.createPeerConnection(rtcConfig, offeringExpectations); 951 assertNotNull(offeringPC); 952 953 ObserverExpectations answeringExpectations = new ObserverExpectations("PCTest:answerer"); 954 PeerConnection answeringPC = factory.createPeerConnection(rtcConfig, answeringExpectations); 955 assertNotNull(answeringPC); 956 957 offeringExpectations.expectRenegotiationNeeded(); 958 DataChannel offeringDC = offeringPC.createDataChannel("offeringDC", new DataChannel.Init()); 959 assertEquals("offeringDC", offeringDC.label()); 960 961 offeringExpectations.setDataChannel(offeringDC); 962 SdpObserverLatch sdpLatch = new SdpObserverLatch(); 963 offeringPC.createOffer(sdpLatch, new MediaConstraints()); 964 assertTrue(sdpLatch.await()); 965 SessionDescription offerSdp = sdpLatch.getSdp(); 966 assertEquals(offerSdp.type, SessionDescription.Type.OFFER); 967 assertFalse(offerSdp.description.isEmpty()); 968 969 sdpLatch = new SdpObserverLatch(); 970 answeringExpectations.expectSignalingChange(SignalingState.HAVE_REMOTE_OFFER); 971 // SCTP DataChannels are announced via OPEN messages over the established 972 // connection (not via SDP), so answeringExpectations can only register 973 // expecting the channel during ICE, below. 974 answeringPC.setRemoteDescription(sdpLatch, offerSdp); 975 assertEquals(PeerConnection.SignalingState.STABLE, offeringPC.signalingState()); 976 assertTrue(sdpLatch.await()); 977 assertNull(sdpLatch.getSdp()); 978 979 sdpLatch = new SdpObserverLatch(); 980 answeringPC.createAnswer(sdpLatch, new MediaConstraints()); 981 assertTrue(sdpLatch.await()); 982 SessionDescription answerSdp = sdpLatch.getSdp(); 983 assertEquals(answerSdp.type, SessionDescription.Type.ANSWER); 984 assertFalse(answerSdp.description.isEmpty()); 985 986 offeringExpectations.expectIceCandidates(2); 987 answeringExpectations.expectIceCandidates(2); 988 989 offeringExpectations.expectIceGatheringChange(IceGatheringState.COMPLETE); 990 answeringExpectations.expectIceGatheringChange(IceGatheringState.COMPLETE); 991 992 sdpLatch = new SdpObserverLatch(); 993 answeringExpectations.expectSignalingChange(SignalingState.STABLE); 994 answeringExpectations.expectConnectionChange(PeerConnectionState.CONNECTING); 995 answeringPC.setLocalDescription(sdpLatch, answerSdp); 996 assertTrue(sdpLatch.await()); 997 assertNull(sdpLatch.getSdp()); 998 999 sdpLatch = new SdpObserverLatch(); 1000 offeringExpectations.expectSignalingChange(SignalingState.HAVE_LOCAL_OFFER); 1001 offeringExpectations.expectConnectionChange(PeerConnectionState.CONNECTING); 1002 offeringPC.setLocalDescription(sdpLatch, offerSdp); 1003 assertTrue(sdpLatch.await()); 1004 assertNull(sdpLatch.getSdp()); 1005 sdpLatch = new SdpObserverLatch(); 1006 offeringExpectations.expectSignalingChange(SignalingState.STABLE); 1007 1008 offeringExpectations.expectIceConnectionChange(IceConnectionState.CHECKING); 1009 offeringExpectations.expectIceConnectionChange(IceConnectionState.CONNECTED); 1010 offeringExpectations.expectStandardizedIceConnectionChange(IceConnectionState.CHECKING); 1011 offeringExpectations.expectStandardizedIceConnectionChange(IceConnectionState.CONNECTED); 1012 offeringExpectations.expectConnectionChange(PeerConnectionState.CONNECTED); 1013 // TODO(bemasc): uncomment once delivery of ICECompleted is reliable 1014 // (https://code.google.com/p/webrtc/issues/detail?id=3021). 1015 answeringExpectations.expectIceConnectionChange(IceConnectionState.CHECKING); 1016 answeringExpectations.expectIceConnectionChange(IceConnectionState.CONNECTED); 1017 answeringExpectations.expectStandardizedIceConnectionChange(IceConnectionState.CHECKING); 1018 answeringExpectations.expectStandardizedIceConnectionChange(IceConnectionState.CONNECTED); 1019 answeringExpectations.expectConnectionChange(PeerConnectionState.CONNECTED); 1020 1021 offeringPC.setRemoteDescription(sdpLatch, answerSdp); 1022 assertTrue(sdpLatch.await()); 1023 assertNull(sdpLatch.getSdp()); 1024 1025 assertEquals(offeringPC.getLocalDescription().type, offerSdp.type); 1026 assertEquals(offeringPC.getRemoteDescription().type, answerSdp.type); 1027 assertEquals(answeringPC.getLocalDescription().type, answerSdp.type); 1028 assertEquals(answeringPC.getRemoteDescription().type, offerSdp.type); 1029 1030 offeringExpectations.expectStateChange(DataChannel.State.OPEN); 1031 // See commentary about SCTP DataChannels above for why this is here. 1032 answeringExpectations.expectDataChannel("offeringDC"); 1033 answeringExpectations.expectStateChange(DataChannel.State.OPEN); 1034 1035 // Wait for at least one ice candidate from the offering PC and forward them to the answering 1036 // PC. 1037 for (IceCandidate candidate : offeringExpectations.getAtLeastOneIceCandidate()) { 1038 answeringPC.addIceCandidate(candidate); 1039 } 1040 1041 // Wait for at least one ice candidate from the answering PC and forward them to the offering 1042 // PC. 1043 for (IceCandidate candidate : answeringExpectations.getAtLeastOneIceCandidate()) { 1044 offeringPC.addIceCandidate(candidate); 1045 } 1046 1047 assertTrue(offeringExpectations.waitForAllExpectationsToBeSatisfied(DEFAULT_TIMEOUT_SECONDS)); 1048 assertTrue(answeringExpectations.waitForAllExpectationsToBeSatisfied(DEFAULT_TIMEOUT_SECONDS)); 1049 1050 assertEquals(PeerConnection.SignalingState.STABLE, offeringPC.signalingState()); 1051 assertEquals(PeerConnection.SignalingState.STABLE, answeringPC.signalingState()); 1052 1053 // Test send & receive UTF-8 text. 1054 answeringExpectations.expectMessage( 1055 ByteBuffer.wrap("hello!".getBytes(Charset.forName("UTF-8"))), false); 1056 DataChannel.Buffer buffer = 1057 new DataChannel.Buffer(ByteBuffer.wrap("hello!".getBytes(Charset.forName("UTF-8"))), false); 1058 assertTrue(offeringExpectations.dataChannel.send(buffer)); 1059 assertTrue(answeringExpectations.waitForAllExpectationsToBeSatisfied(DEFAULT_TIMEOUT_SECONDS)); 1060 1061 // Construct this binary message two different ways to ensure no 1062 // shortcuts are taken. 1063 ByteBuffer expectedBinaryMessage = ByteBuffer.allocateDirect(5); 1064 for (byte i = 1; i < 6; ++i) { 1065 expectedBinaryMessage.put(i); 1066 } 1067 expectedBinaryMessage.flip(); 1068 offeringExpectations.expectMessage(expectedBinaryMessage, true); 1069 assertTrue(answeringExpectations.dataChannel.send( 1070 new DataChannel.Buffer(ByteBuffer.wrap(new byte[] {1, 2, 3, 4, 5}), true))); 1071 assertTrue(offeringExpectations.waitForAllExpectationsToBeSatisfied(DEFAULT_TIMEOUT_SECONDS)); 1072 1073 offeringExpectations.expectStateChange(DataChannel.State.CLOSING); 1074 answeringExpectations.expectStateChange(DataChannel.State.CLOSING); 1075 offeringExpectations.expectStateChange(DataChannel.State.CLOSED); 1076 answeringExpectations.expectStateChange(DataChannel.State.CLOSED); 1077 answeringExpectations.dataChannel.close(); 1078 offeringExpectations.dataChannel.close(); 1079 assertTrue(offeringExpectations.waitForAllExpectationsToBeSatisfied(DEFAULT_TIMEOUT_SECONDS)); 1080 assertTrue(answeringExpectations.waitForAllExpectationsToBeSatisfied(DEFAULT_TIMEOUT_SECONDS)); 1081 1082 // Free the Java-land objects and collect them. 1083 shutdownPC(offeringPC, offeringExpectations); 1084 offeringPC = null; 1085 shutdownPC(answeringPC, answeringExpectations); 1086 answeringPC = null; 1087 factory.dispose(); 1088 System.gc(); 1089 } 1090 1091 // Tests that ICE candidates that are not allowed by an ICE transport type, thus not being 1092 // signaled to the gathering PeerConnection, can be surfaced via configuration if allowed by the 1093 // new ICE transport type, when RTCConfiguration.surfaceIceCandidatesOnIceTransportTypeChanged is 1094 // true. 1095 @Test 1096 @SmallTest testSurfaceIceCandidatesWhenIceTransportTypeChanged()1097 public void testSurfaceIceCandidatesWhenIceTransportTypeChanged() throws Exception { 1098 // For this test, we only need one PeerConnection to observe the behavior of gathering, and we 1099 // create only the offering PC below. 1100 // 1101 // Allow loopback interfaces too since our Android devices often don't 1102 // have those. 1103 PeerConnectionFactory.Options options = new PeerConnectionFactory.Options(); 1104 options.networkIgnoreMask = 0; 1105 PeerConnectionFactory factory = 1106 PeerConnectionFactory.builder().setOptions(options).createPeerConnectionFactory(); 1107 1108 PeerConnection.RTCConfiguration rtcConfig = 1109 new PeerConnection.RTCConfiguration(Arrays.asList()); 1110 // NONE would prevent any candidate being signaled to the PC. 1111 rtcConfig.iceTransportsType = PeerConnection.IceTransportsType.NONE; 1112 // We must have the continual gathering enabled to allow the surfacing of candidates on the ICE 1113 // transport type change. 1114 rtcConfig.continualGatheringPolicy = PeerConnection.ContinualGatheringPolicy.GATHER_CONTINUALLY; 1115 rtcConfig.surfaceIceCandidatesOnIceTransportTypeChanged = true; 1116 1117 ObserverExpectations offeringExpectations = new ObserverExpectations("PCTest:offerer"); 1118 PeerConnection offeringPC = factory.createPeerConnection(rtcConfig, offeringExpectations); 1119 assertNotNull(offeringPC); 1120 1121 // Create a data channel and set local description to kick off the ICE candidate gathering. 1122 offeringExpectations.expectRenegotiationNeeded(); 1123 DataChannel offeringDC = offeringPC.createDataChannel("offeringDC", new DataChannel.Init()); 1124 assertEquals("offeringDC", offeringDC.label()); 1125 1126 offeringExpectations.setDataChannel(offeringDC); 1127 SdpObserverLatch sdpLatch = new SdpObserverLatch(); 1128 offeringPC.createOffer(sdpLatch, new MediaConstraints()); 1129 assertTrue(sdpLatch.await()); 1130 SessionDescription offerSdp = sdpLatch.getSdp(); 1131 assertEquals(offerSdp.type, SessionDescription.Type.OFFER); 1132 assertFalse(offerSdp.description.isEmpty()); 1133 1134 sdpLatch = new SdpObserverLatch(); 1135 offeringExpectations.expectSignalingChange(SignalingState.HAVE_LOCAL_OFFER); 1136 offeringPC.setLocalDescription(sdpLatch, offerSdp); 1137 assertTrue(sdpLatch.await()); 1138 assertNull(sdpLatch.getSdp()); 1139 1140 assertEquals(offeringPC.getLocalDescription().type, offerSdp.type); 1141 1142 // Wait until we satisfy all expectations in the setup. 1143 assertTrue(offeringExpectations.waitForAllExpectationsToBeSatisfied(DEFAULT_TIMEOUT_SECONDS)); 1144 1145 // Add the expectation of gathering at least one candidate, which should however fail because of 1146 // the transport type NONE. 1147 offeringExpectations.expectIceCandidates(1); 1148 assertFalse(offeringExpectations.waitForAllExpectationsToBeSatisfied(SHORT_TIMEOUT_SECONDS)); 1149 1150 // Change the transport type and we should be able to meet the expectation of gathering this 1151 // time. 1152 rtcConfig.iceTransportsType = PeerConnection.IceTransportsType.ALL; 1153 offeringPC.setConfiguration(rtcConfig); 1154 assertTrue(offeringExpectations.waitForAllExpectationsToBeSatisfied(DEFAULT_TIMEOUT_SECONDS)); 1155 } 1156 1157 @Test 1158 @MediumTest testTrackRemovalAndAddition()1159 public void testTrackRemovalAndAddition() throws Exception { 1160 // Allow loopback interfaces too since our Android devices often don't 1161 // have those. 1162 PeerConnectionFactory.Options options = new PeerConnectionFactory.Options(); 1163 options.networkIgnoreMask = 0; 1164 PeerConnectionFactory factory = PeerConnectionFactory.builder() 1165 .setOptions(options) 1166 .setVideoEncoderFactory(new SoftwareVideoEncoderFactory()) 1167 .setVideoDecoderFactory(new SoftwareVideoDecoderFactory()) 1168 .createPeerConnectionFactory(); 1169 1170 List<PeerConnection.IceServer> iceServers = new ArrayList<>(); 1171 iceServers.add( 1172 PeerConnection.IceServer.builder("stun:stun.l.google.com:19302").createIceServer()); 1173 1174 PeerConnection.RTCConfiguration rtcConfig = new PeerConnection.RTCConfiguration(iceServers); 1175 rtcConfig.enableDtlsSrtp = true; 1176 1177 ObserverExpectations offeringExpectations = new ObserverExpectations("PCTest:offerer"); 1178 PeerConnection offeringPC = factory.createPeerConnection(rtcConfig, offeringExpectations); 1179 assertNotNull(offeringPC); 1180 1181 ObserverExpectations answeringExpectations = new ObserverExpectations("PCTest:answerer"); 1182 PeerConnection answeringPC = factory.createPeerConnection(rtcConfig, answeringExpectations); 1183 assertNotNull(answeringPC); 1184 1185 // We want to use the same camera for offerer & answerer, so create it here 1186 // instead of in addTracksToPC. 1187 final CameraEnumerator enumerator = new Camera1Enumerator(false /* captureToTexture */); 1188 final VideoCapturer videoCapturer = 1189 enumerator.createCapturer(enumerator.getDeviceNames()[0], null /* eventsHandler */); 1190 final SurfaceTextureHelper surfaceTextureHelper = 1191 SurfaceTextureHelper.create("SurfaceTextureHelper", /* sharedContext= */ null); 1192 final VideoSource videoSource = factory.createVideoSource(/* isScreencast= */ false); 1193 videoCapturer.initialize(surfaceTextureHelper, InstrumentationRegistry.getTargetContext(), 1194 videoSource.getCapturerObserver()); 1195 videoCapturer.startCapture(640, 480, 30); 1196 1197 // Add offerer media stream. 1198 offeringExpectations.expectRenegotiationNeeded(); 1199 WeakReference<MediaStream> oLMS = 1200 addTracksToPC(factory, offeringPC, videoSource, "offeredMediaStream", "offeredVideoTrack", 1201 "offeredAudioTrack", new ExpectedResolutionSetter(answeringExpectations)); 1202 1203 offeringExpectations.expectAddTrack(2); 1204 answeringExpectations.expectAddTrack(2); 1205 // Create offer. 1206 SdpObserverLatch sdpLatch = new SdpObserverLatch(); 1207 offeringPC.createOffer(sdpLatch, new MediaConstraints()); 1208 assertTrue(sdpLatch.await()); 1209 SessionDescription offerSdp = sdpLatch.getSdp(); 1210 assertEquals(offerSdp.type, SessionDescription.Type.OFFER); 1211 assertFalse(offerSdp.description.isEmpty()); 1212 1213 // Set local description for offerer. 1214 sdpLatch = new SdpObserverLatch(); 1215 offeringExpectations.expectSignalingChange(SignalingState.HAVE_LOCAL_OFFER); 1216 offeringExpectations.expectIceCandidates(2); 1217 offeringExpectations.expectIceGatheringChange(IceGatheringState.COMPLETE); 1218 offeringExpectations.expectConnectionChange(PeerConnectionState.CONNECTING); 1219 offeringPC.setLocalDescription(sdpLatch, offerSdp); 1220 assertTrue(sdpLatch.await()); 1221 assertNull(sdpLatch.getSdp()); 1222 1223 // Set remote description for answerer. 1224 sdpLatch = new SdpObserverLatch(); 1225 answeringExpectations.expectSignalingChange(SignalingState.HAVE_REMOTE_OFFER); 1226 answeringExpectations.expectAddStream("offeredMediaStream"); 1227 answeringPC.setRemoteDescription(sdpLatch, offerSdp); 1228 assertTrue(sdpLatch.await()); 1229 assertNull(sdpLatch.getSdp()); 1230 1231 // Add answerer media stream. 1232 answeringExpectations.expectRenegotiationNeeded(); 1233 WeakReference<MediaStream> aLMS = addTracksToPC(factory, answeringPC, videoSource, 1234 "answeredMediaStream", "answeredVideoTrack", "answeredAudioTrack", 1235 new ExpectedResolutionSetter(offeringExpectations)); 1236 1237 // Create answer. 1238 sdpLatch = new SdpObserverLatch(); 1239 answeringPC.createAnswer(sdpLatch, new MediaConstraints()); 1240 assertTrue(sdpLatch.await()); 1241 SessionDescription answerSdp = sdpLatch.getSdp(); 1242 assertEquals(answerSdp.type, SessionDescription.Type.ANSWER); 1243 assertFalse(answerSdp.description.isEmpty()); 1244 1245 // Set local description for answerer. 1246 sdpLatch = new SdpObserverLatch(); 1247 answeringExpectations.expectSignalingChange(SignalingState.STABLE); 1248 answeringExpectations.expectIceCandidates(2); 1249 answeringExpectations.expectIceGatheringChange(IceGatheringState.COMPLETE); 1250 answeringExpectations.expectConnectionChange(PeerConnectionState.CONNECTING); 1251 answeringPC.setLocalDescription(sdpLatch, answerSdp); 1252 assertTrue(sdpLatch.await()); 1253 assertNull(sdpLatch.getSdp()); 1254 1255 // Set remote description for offerer. 1256 sdpLatch = new SdpObserverLatch(); 1257 offeringExpectations.expectSignalingChange(SignalingState.STABLE); 1258 offeringExpectations.expectAddStream("answeredMediaStream"); 1259 1260 offeringExpectations.expectIceConnectionChange(IceConnectionState.CHECKING); 1261 offeringExpectations.expectIceConnectionChange(IceConnectionState.CONNECTED); 1262 offeringExpectations.expectStandardizedIceConnectionChange(IceConnectionState.CHECKING); 1263 offeringExpectations.expectStandardizedIceConnectionChange(IceConnectionState.CONNECTED); 1264 offeringExpectations.expectConnectionChange(PeerConnectionState.CONNECTED); 1265 // TODO(bemasc): uncomment once delivery of ICECompleted is reliable 1266 // (https://code.google.com/p/webrtc/issues/detail?id=3021). 1267 // 1268 // offeringExpectations.expectIceConnectionChange( 1269 // IceConnectionState.COMPLETED); 1270 answeringExpectations.expectIceConnectionChange(IceConnectionState.CHECKING); 1271 answeringExpectations.expectIceConnectionChange(IceConnectionState.CONNECTED); 1272 answeringExpectations.expectStandardizedIceConnectionChange(IceConnectionState.CHECKING); 1273 answeringExpectations.expectStandardizedIceConnectionChange(IceConnectionState.CONNECTED); 1274 answeringExpectations.expectConnectionChange(PeerConnectionState.CONNECTED); 1275 1276 offeringPC.setRemoteDescription(sdpLatch, answerSdp); 1277 assertTrue(sdpLatch.await()); 1278 assertNull(sdpLatch.getSdp()); 1279 1280 // Wait for at least one ice candidate from the offering PC and forward them to the answering 1281 // PC. 1282 for (IceCandidate candidate : offeringExpectations.getAtLeastOneIceCandidate()) { 1283 answeringPC.addIceCandidate(candidate); 1284 } 1285 1286 // Wait for at least one ice candidate from the answering PC and forward them to the offering 1287 // PC. 1288 for (IceCandidate candidate : answeringExpectations.getAtLeastOneIceCandidate()) { 1289 offeringPC.addIceCandidate(candidate); 1290 } 1291 1292 // Wait for one frame of the correct size to be delivered. 1293 // Otherwise we could get a dummy black frame of unexpcted size when the 1294 // video track is removed. 1295 offeringExpectations.expectFramesDelivered(1); 1296 answeringExpectations.expectFramesDelivered(1); 1297 1298 assertTrue(offeringExpectations.waitForAllExpectationsToBeSatisfied(DEFAULT_TIMEOUT_SECONDS)); 1299 assertTrue(answeringExpectations.waitForAllExpectationsToBeSatisfied(DEFAULT_TIMEOUT_SECONDS)); 1300 1301 assertEquals(PeerConnection.SignalingState.STABLE, offeringPC.signalingState()); 1302 assertEquals(PeerConnection.SignalingState.STABLE, answeringPC.signalingState()); 1303 1304 // Now do another negotiation, removing the video track from one peer. 1305 // This previously caused a crash on pc.dispose(). 1306 // See: https://bugs.chromium.org/p/webrtc/issues/detail?id=5128 1307 VideoTrack offererVideoTrack = oLMS.get().videoTracks.get(0); 1308 // Note that when we call removeTrack, we regain responsibility for 1309 // disposing of the track. 1310 offeringExpectations.expectRenegotiationNeeded(); 1311 oLMS.get().removeTrack(offererVideoTrack); 1312 negotiate(offeringPC, offeringExpectations, answeringPC, answeringExpectations); 1313 1314 // Make sure the track was really removed. 1315 MediaStream aRMS = answeringExpectations.gotRemoteStreams.iterator().next(); 1316 assertTrue(aRMS.videoTracks.isEmpty()); 1317 1318 // Add the video track to test if the answeringPC will create a new track 1319 // for the updated remote description. 1320 offeringExpectations.expectRenegotiationNeeded(); 1321 oLMS.get().addTrack(offererVideoTrack); 1322 // The answeringPC sets the updated remote description with a track added. 1323 // So the onAddTrack callback is expected to be called once. 1324 answeringExpectations.expectAddTrack(1); 1325 offeringExpectations.expectAddTrack(0); 1326 negotiate(offeringPC, offeringExpectations, answeringPC, answeringExpectations); 1327 1328 // Finally, remove both the audio and video tracks, which should completely 1329 // remove the remote stream. This used to trigger an assert. 1330 // See: https://bugs.chromium.org/p/webrtc/issues/detail?id=5128 1331 offeringExpectations.expectRenegotiationNeeded(); 1332 oLMS.get().removeTrack(offererVideoTrack); 1333 AudioTrack offererAudioTrack = oLMS.get().audioTracks.get(0); 1334 offeringExpectations.expectRenegotiationNeeded(); 1335 oLMS.get().removeTrack(offererAudioTrack); 1336 1337 answeringExpectations.expectRemoveStream("offeredMediaStream"); 1338 negotiate(offeringPC, offeringExpectations, answeringPC, answeringExpectations); 1339 1340 // Make sure the stream was really removed. 1341 assertTrue(answeringExpectations.gotRemoteStreams.isEmpty()); 1342 1343 // Free the Java-land objects and collect them. 1344 shutdownPC(offeringPC, offeringExpectations); 1345 offeringPC = null; 1346 shutdownPC(answeringPC, answeringExpectations); 1347 answeringPC = null; 1348 offererVideoTrack.dispose(); 1349 offererAudioTrack.dispose(); 1350 videoCapturer.stopCapture(); 1351 videoCapturer.dispose(); 1352 videoSource.dispose(); 1353 surfaceTextureHelper.dispose(); 1354 factory.dispose(); 1355 System.gc(); 1356 } 1357 1358 /** 1359 * Test that a Java MediaStream is updated when the native stream is. 1360 * <p> 1361 * Specifically, test that when remote tracks are indicated as being added or 1362 * removed from a MediaStream (via "a=ssrc" or "a=msid" in a remote 1363 * description), the existing remote MediaStream object is updated. 1364 * <p> 1365 * This test starts with just an audio track, adds a video track, then 1366 * removes it. It only applies remote offers, which is sufficient to test 1367 * this functionality and simplifies the test. This means that no media will 1368 * actually be sent/received; we're just testing that the Java MediaStream 1369 * object gets updated when the native object changes. 1370 */ 1371 @Test 1372 @MediumTest testRemoteStreamUpdatedWhenTracksAddedOrRemoved()1373 public void testRemoteStreamUpdatedWhenTracksAddedOrRemoved() throws Exception { 1374 PeerConnectionFactory factory = PeerConnectionFactory.builder() 1375 .setVideoEncoderFactory(new SoftwareVideoEncoderFactory()) 1376 .setVideoDecoderFactory(new SoftwareVideoDecoderFactory()) 1377 .createPeerConnectionFactory(); 1378 1379 // This test is fine with no ICE servers. 1380 List<PeerConnection.IceServer> iceServers = new ArrayList<>(); 1381 1382 // Use OfferToReceiveAudio/Video to ensure every offer has an audio and 1383 // video m= section. Simplifies the test because it means we don't have to 1384 // actually apply the offer to "offeringPC"; it's just used as an SDP 1385 // factory. 1386 MediaConstraints offerConstraints = new MediaConstraints(); 1387 offerConstraints.mandatory.add( 1388 new MediaConstraints.KeyValuePair("OfferToReceiveAudio", "true")); 1389 offerConstraints.mandatory.add( 1390 new MediaConstraints.KeyValuePair("OfferToReceiveVideo", "true")); 1391 1392 // This PeerConnection will only be used to generate offers. 1393 ObserverExpectations offeringExpectations = new ObserverExpectations("offerer"); 1394 PeerConnection offeringPC = factory.createPeerConnection(iceServers, offeringExpectations); 1395 assertNotNull(offeringPC); 1396 1397 ObserverExpectations expectations = new ObserverExpectations("PC under test"); 1398 PeerConnection pcUnderTest = factory.createPeerConnection(iceServers, expectations); 1399 assertNotNull(pcUnderTest); 1400 1401 // Add offerer media stream with just an audio track. 1402 MediaStream localStream = factory.createLocalMediaStream("stream"); 1403 AudioTrack localAudioTrack = 1404 factory.createAudioTrack("audio", factory.createAudioSource(new MediaConstraints())); 1405 localStream.addTrack(localAudioTrack); 1406 // TODO(deadbeef): Use addTrack once that's available. 1407 offeringExpectations.expectRenegotiationNeeded(); 1408 offeringPC.addStream(localStream); 1409 // Create offer. 1410 SdpObserverLatch sdpLatch = new SdpObserverLatch(); 1411 offeringPC.createOffer(sdpLatch, offerConstraints); 1412 assertTrue(sdpLatch.await()); 1413 SessionDescription offerSdp = sdpLatch.getSdp(); 1414 1415 // Apply remote offer to PC under test. 1416 sdpLatch = new SdpObserverLatch(); 1417 expectations.expectSignalingChange(SignalingState.HAVE_REMOTE_OFFER); 1418 expectations.expectAddStream("stream"); 1419 pcUnderTest.setRemoteDescription(sdpLatch, offerSdp); 1420 assertTrue(sdpLatch.await()); 1421 // Sanity check that we get one remote stream with one audio track. 1422 MediaStream remoteStream = expectations.gotRemoteStreams.iterator().next(); 1423 assertEquals(remoteStream.audioTracks.size(), 1); 1424 assertEquals(remoteStream.videoTracks.size(), 0); 1425 1426 // Add a video track... 1427 final CameraEnumerator enumerator = new Camera1Enumerator(false /* captureToTexture */); 1428 final VideoCapturer videoCapturer = 1429 enumerator.createCapturer(enumerator.getDeviceNames()[0], null /* eventsHandler */); 1430 final SurfaceTextureHelper surfaceTextureHelper = 1431 SurfaceTextureHelper.create("SurfaceTextureHelper", /* sharedContext= */ null); 1432 final VideoSource videoSource = factory.createVideoSource(/* isScreencast= */ false); 1433 videoCapturer.initialize(surfaceTextureHelper, InstrumentationRegistry.getTargetContext(), 1434 videoSource.getCapturerObserver()); 1435 VideoTrack videoTrack = factory.createVideoTrack("video", videoSource); 1436 offeringExpectations.expectRenegotiationNeeded(); 1437 localStream.addTrack(videoTrack); 1438 // ... and create an updated offer. 1439 sdpLatch = new SdpObserverLatch(); 1440 offeringPC.createOffer(sdpLatch, offerConstraints); 1441 assertTrue(sdpLatch.await()); 1442 offerSdp = sdpLatch.getSdp(); 1443 1444 // Apply remote offer with new video track to PC under test. 1445 sdpLatch = new SdpObserverLatch(); 1446 pcUnderTest.setRemoteDescription(sdpLatch, offerSdp); 1447 assertTrue(sdpLatch.await()); 1448 // The remote stream should now have a video track. 1449 assertEquals(remoteStream.audioTracks.size(), 1); 1450 assertEquals(remoteStream.videoTracks.size(), 1); 1451 1452 // Finally, create another offer with the audio track removed. 1453 offeringExpectations.expectRenegotiationNeeded(); 1454 localStream.removeTrack(localAudioTrack); 1455 localAudioTrack.dispose(); 1456 sdpLatch = new SdpObserverLatch(); 1457 offeringPC.createOffer(sdpLatch, offerConstraints); 1458 assertTrue(sdpLatch.await()); 1459 offerSdp = sdpLatch.getSdp(); 1460 1461 // Apply remote offer with just a video track to PC under test. 1462 sdpLatch = new SdpObserverLatch(); 1463 pcUnderTest.setRemoteDescription(sdpLatch, offerSdp); 1464 assertTrue(sdpLatch.await()); 1465 // The remote stream should no longer have an audio track. 1466 assertEquals(remoteStream.audioTracks.size(), 0); 1467 assertEquals(remoteStream.videoTracks.size(), 1); 1468 1469 // Free the Java-land objects. Video capturer and source aren't owned by 1470 // the PeerConnection and need to be disposed separately. 1471 // TODO(deadbeef): Should all these events really occur on disposal? 1472 // "Gathering complete" is especially odd since gathering never started. 1473 // Note that this test isn't meant to test these events, but we must do 1474 // this or otherwise it will crash. 1475 offeringExpectations.expectIceConnectionChange(IceConnectionState.CLOSED); 1476 offeringExpectations.expectStandardizedIceConnectionChange(IceConnectionState.CLOSED); 1477 offeringExpectations.expectSignalingChange(SignalingState.CLOSED); 1478 offeringExpectations.expectIceGatheringChange(IceGatheringState.COMPLETE); 1479 offeringPC.dispose(); 1480 expectations.expectIceConnectionChange(IceConnectionState.CLOSED); 1481 expectations.expectStandardizedIceConnectionChange(IceConnectionState.CLOSED); 1482 expectations.expectSignalingChange(SignalingState.CLOSED); 1483 expectations.expectIceGatheringChange(IceGatheringState.COMPLETE); 1484 pcUnderTest.dispose(); 1485 videoCapturer.dispose(); 1486 videoSource.dispose(); 1487 surfaceTextureHelper.dispose(); 1488 factory.dispose(); 1489 } 1490 negotiate(PeerConnection offeringPC, ObserverExpectations offeringExpectations, PeerConnection answeringPC, ObserverExpectations answeringExpectations)1491 private static void negotiate(PeerConnection offeringPC, 1492 ObserverExpectations offeringExpectations, PeerConnection answeringPC, 1493 ObserverExpectations answeringExpectations) { 1494 // Create offer. 1495 SdpObserverLatch sdpLatch = new SdpObserverLatch(); 1496 offeringPC.createOffer(sdpLatch, new MediaConstraints()); 1497 assertTrue(sdpLatch.await()); 1498 SessionDescription offerSdp = sdpLatch.getSdp(); 1499 assertEquals(offerSdp.type, SessionDescription.Type.OFFER); 1500 assertFalse(offerSdp.description.isEmpty()); 1501 1502 // Set local description for offerer. 1503 sdpLatch = new SdpObserverLatch(); 1504 offeringExpectations.expectSignalingChange(SignalingState.HAVE_LOCAL_OFFER); 1505 offeringPC.setLocalDescription(sdpLatch, offerSdp); 1506 assertTrue(sdpLatch.await()); 1507 assertNull(sdpLatch.getSdp()); 1508 1509 // Set remote description for answerer. 1510 sdpLatch = new SdpObserverLatch(); 1511 answeringExpectations.expectSignalingChange(SignalingState.HAVE_REMOTE_OFFER); 1512 answeringPC.setRemoteDescription(sdpLatch, offerSdp); 1513 assertTrue(sdpLatch.await()); 1514 assertNull(sdpLatch.getSdp()); 1515 1516 // Create answer. 1517 sdpLatch = new SdpObserverLatch(); 1518 answeringPC.createAnswer(sdpLatch, new MediaConstraints()); 1519 assertTrue(sdpLatch.await()); 1520 SessionDescription answerSdp = sdpLatch.getSdp(); 1521 assertEquals(answerSdp.type, SessionDescription.Type.ANSWER); 1522 assertFalse(answerSdp.description.isEmpty()); 1523 1524 // Set local description for answerer. 1525 sdpLatch = new SdpObserverLatch(); 1526 answeringExpectations.expectSignalingChange(SignalingState.STABLE); 1527 answeringPC.setLocalDescription(sdpLatch, answerSdp); 1528 assertTrue(sdpLatch.await()); 1529 assertNull(sdpLatch.getSdp()); 1530 1531 // Set remote description for offerer. 1532 sdpLatch = new SdpObserverLatch(); 1533 offeringExpectations.expectSignalingChange(SignalingState.STABLE); 1534 offeringPC.setRemoteDescription(sdpLatch, answerSdp); 1535 assertTrue(sdpLatch.await()); 1536 assertNull(sdpLatch.getSdp()); 1537 } 1538 1539 @SuppressWarnings("deprecation") // TODO(sakal): getStats is deprecated shutdownPC(PeerConnection pc, ObserverExpectations expectations)1540 private static void shutdownPC(PeerConnection pc, ObserverExpectations expectations) { 1541 if (expectations.dataChannel != null) { 1542 expectations.dataChannel.unregisterObserver(); 1543 expectations.dataChannel.dispose(); 1544 } 1545 1546 // Call getStats (old implementation) before shutting down PC. 1547 expectations.expectOldStatsCallback(); 1548 assertTrue(pc.getStats(expectations, null /* track */)); 1549 assertTrue(expectations.waitForAllExpectationsToBeSatisfied(DEFAULT_TIMEOUT_SECONDS)); 1550 1551 // Call the new getStats implementation as well. 1552 expectations.expectNewStatsCallback(); 1553 pc.getStats(expectations); 1554 assertTrue(expectations.waitForAllExpectationsToBeSatisfied(DEFAULT_TIMEOUT_SECONDS)); 1555 1556 expectations.expectIceConnectionChange(IceConnectionState.CLOSED); 1557 expectations.expectStandardizedIceConnectionChange(IceConnectionState.CLOSED); 1558 expectations.expectConnectionChange(PeerConnectionState.CLOSED); 1559 expectations.expectSignalingChange(SignalingState.CLOSED); 1560 pc.close(); 1561 assertTrue(expectations.waitForAllExpectationsToBeSatisfied(DEFAULT_TIMEOUT_SECONDS)); 1562 1563 // Call getStats (old implementation) after calling close(). Should still 1564 // work. 1565 expectations.expectOldStatsCallback(); 1566 assertTrue(pc.getStats(expectations, null /* track */)); 1567 assertTrue(expectations.waitForAllExpectationsToBeSatisfied(DEFAULT_TIMEOUT_SECONDS)); 1568 1569 Logging.d(TAG, "FYI stats: "); 1570 int reportIndex = -1; 1571 for (StatsReport[] reports : expectations.takeStatsReports()) { 1572 Logging.d(TAG, " Report #" + (++reportIndex)); 1573 for (int i = 0; i < reports.length; ++i) { 1574 Logging.d(TAG, " " + reports[i].toString()); 1575 } 1576 } 1577 assertEquals(1, reportIndex); 1578 Logging.d(TAG, "End stats."); 1579 1580 pc.dispose(); 1581 } 1582 } 1583