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