diff --git a/pc/peerconnection.cc b/pc/peerconnection.cc index b032043206..533e64845e 100644 --- a/pc/peerconnection.cc +++ b/pc/peerconnection.cc @@ -1185,7 +1185,8 @@ PeerConnection::FindFirstTransceiverForAddedTrack( if (!transceiver->sender()->track() && cricket::MediaTypeToString(transceiver->media_type()) == track->kind() && - !transceiver->internal()->has_ever_been_used_to_send()) { + !transceiver->internal()->has_ever_been_used_to_send() && + !transceiver->stopped()) { return transceiver; } } @@ -2228,12 +2229,16 @@ RTCError PeerConnection::ApplyRemoteDescription( // the RTCSessionDescription: If direction is sendrecv or recvonly, and // transceiver's current direction is neither sendrecv nor recvonly, // process the addition of a remote track for the media description. + // + // Create a sync label in the case of an unsignalled msid. + std::string sync_label = rtc::CreateRandomUuid(); + if (!media_desc->streams().empty()) { + sync_label = media_desc->streams()[0].sync_label; + } if (RtpTransceiverDirectionHasRecv(local_direction) && (!transceiver->current_direction() || !RtpTransceiverDirectionHasRecv( - *transceiver->current_direction())) && - !media_desc->streams().empty()) { - const std::string& sync_label = media_desc->streams()[0].sync_label; + *transceiver->current_direction()))) { rtc::scoped_refptr stream = remote_streams_->find(sync_label); if (!stream) { @@ -2259,9 +2264,14 @@ RTCError PeerConnection::ApplyRemoteDescription( if (content->rejected && !transceiver->stopped()) { transceiver->Stop(); } - if (!content->rejected && !media_desc->streams().empty()) { - transceiver->internal()->receiver_internal()->SetupMediaChannel( - media_desc->streams()[0].first_ssrc()); + if (!content->rejected && + RtpTransceiverDirectionHasRecv(local_direction)) { + // Set ssrc to 0 in the case of an unsignalled ssrc. + uint32_t ssrc = 0; + if (!media_desc->streams().empty()) { + ssrc = media_desc->streams()[0].first_ssrc(); + } + transceiver->internal()->receiver_internal()->SetupMediaChannel(ssrc); } } // Once all processing has finished, fire off callbacks. diff --git a/pc/peerconnection_integrationtest.cc b/pc/peerconnection_integrationtest.cc index 788b432edd..d5037a82b1 100644 --- a/pc/peerconnection_integrationtest.cc +++ b/pc/peerconnection_integrationtest.cc @@ -42,6 +42,7 @@ #include "pc/mediasession.h" #include "pc/peerconnection.h" #include "pc/peerconnectionfactory.h" +#include "pc/rtpmediautils.h" #include "pc/sessiondescription.h" #include "pc/test/fakeaudiocapturemodule.h" #include "pc/test/fakeperiodicvideocapturer.h" @@ -61,6 +62,7 @@ using cricket::FakeWebRtcVideoEncoder; using cricket::FakeWebRtcVideoEncoderFactory; using cricket::MediaContentDescription; using rtc::SocketAddress; +using ::testing::Combine; using ::testing::ElementsAre; using ::testing::Values; using webrtc::DataBuffer; @@ -86,6 +88,10 @@ using webrtc::PeerConnectionProxy; using webrtc::RTCErrorType; using webrtc::RtpSenderInterface; using webrtc::RtpReceiverInterface; +using webrtc::RtpSenderInterface; +using webrtc::RtpTransceiverDirection; +using webrtc::RtpTransceiverInit; +using webrtc::RtpTransceiverInterface; using webrtc::SdpSemantics; using webrtc::SdpType; using webrtc::SessionDescriptionInterface; @@ -179,7 +185,7 @@ class MockRtpReceiverObserver : public webrtc::RtpReceiverObserverInterface { // encoders/decoders, though they aren't used by default since they don't // advertise support of any codecs. // TODO(steveanton): See how this could become a subclass of -// PeerConnectionWrapper defined in peerconnectionwrapper.h . +// PeerConnectionWrapper defined in peerconnectionwrapper.h. class PeerConnectionWrapper : public webrtc::PeerConnectionObserver, public SignalingMessageReceiver { public: @@ -204,54 +210,6 @@ class PeerConnectionWrapper : public webrtc::PeerConnectionObserver, return client; } - static PeerConnectionWrapper* CreateWithConfig( - const std::string& debug_name, - const PeerConnectionInterface::RTCConfiguration& config, - rtc::Thread* network_thread, - rtc::Thread* worker_thread) { - std::unique_ptr cert_generator( - new FakeRTCCertificateGenerator()); - PeerConnectionWrapper* client(new PeerConnectionWrapper(debug_name)); - if (!client->Init(nullptr, nullptr, &config, std::move(cert_generator), - network_thread, worker_thread)) { - delete client; - return nullptr; - } - return client; - } - - static PeerConnectionWrapper* CreateWithOptions( - const std::string& debug_name, - const PeerConnectionFactory::Options& options, - rtc::Thread* network_thread, - rtc::Thread* worker_thread) { - std::unique_ptr cert_generator( - new FakeRTCCertificateGenerator()); - PeerConnectionWrapper* client(new PeerConnectionWrapper(debug_name)); - if (!client->Init(nullptr, &options, nullptr, std::move(cert_generator), - network_thread, worker_thread)) { - delete client; - return nullptr; - } - return client; - } - - static PeerConnectionWrapper* CreateWithConstraints( - const std::string& debug_name, - const MediaConstraintsInterface* constraints, - rtc::Thread* network_thread, - rtc::Thread* worker_thread) { - std::unique_ptr cert_generator( - new FakeRTCCertificateGenerator()); - PeerConnectionWrapper* client(new PeerConnectionWrapper(debug_name)); - if (!client->Init(constraints, nullptr, nullptr, std::move(cert_generator), - network_thread, worker_thread)) { - delete client; - return nullptr; - } - return client; - } - webrtc::PeerConnectionFactoryInterface* pc_factory() const { return peer_connection_factory_.get(); } @@ -291,6 +249,13 @@ class PeerConnectionWrapper : public webrtc::PeerConnectionObserver, generated_sdp_munger_ = std::move(munger); } + // Set a callback to be invoked when a remote offer is received via the fake + // signaling channel. This provides an opportunity to change the + // PeerConnection state before an answer is created and sent to the caller. + void SetRemoteOfferHandler(std::function handler) { + remote_offer_handler_ = std::move(handler); + } + // Every ICE connection state in order that has been seen by the observer. std::vector ice_connection_state_history() const { @@ -365,6 +330,16 @@ class PeerConnectionWrapper : public webrtc::PeerConnectionObserver, return receivers; } + rtc::scoped_refptr GetFirstTransceiverOfType( + cricket::MediaType media_type) { + for (auto transceiver : pc()->GetTransceivers()) { + if (transceiver->receiver()->media_type() == media_type) { + return transceiver; + } + } + return nullptr; + } + bool SignalingStateStable() { return pc()->signaling_state() == webrtc::PeerConnectionInterface::kStable; } @@ -616,6 +591,9 @@ class PeerConnectionWrapper : public webrtc::PeerConnectionObserver, if (options) { peer_connection_factory_->SetOptions(*options); } + if (config) { + sdp_semantics_ = config->sdp_semantics; + } peer_connection_ = CreatePeerConnection(std::move(port_allocator), constraints, config, std::move(cert_generator)); @@ -697,6 +675,9 @@ class PeerConnectionWrapper : public webrtc::PeerConnectionObserver, // Setting a remote description may have changed the number of receivers, // so reset the receiver observers. ResetRtpReceiverObservers(); + if (remote_offer_handler_) { + remote_offer_handler_(); + } auto answer = CreateAnswer(); ASSERT_NE(nullptr, answer); EXPECT_TRUE(SetLocalDescriptionAndSendSdpMessage(std::move(answer))); @@ -758,6 +739,9 @@ class PeerConnectionWrapper : public webrtc::PeerConnectionObserver, std::string sdp; EXPECT_TRUE(desc->ToString(&sdp)); pc()->SetLocalDescription(observer, desc.release()); + if (sdp_semantics_ == SdpSemantics::kUnifiedPlan) { + RemoveUnusedVideoRenderers(); + } // As mentioned above, we need to send the message immediately after // SetLocalDescription. SendSdpMessage(type, sdp); @@ -770,10 +754,43 @@ class PeerConnectionWrapper : public webrtc::PeerConnectionObserver, new rtc::RefCountedObject()); RTC_LOG(LS_INFO) << debug_name_ << ": SetRemoteDescription"; pc()->SetRemoteDescription(observer, desc.release()); + if (sdp_semantics_ == SdpSemantics::kUnifiedPlan) { + RemoveUnusedVideoRenderers(); + } EXPECT_TRUE_WAIT(observer->called(), kDefaultTimeout); return observer->result(); } + // This is a work around to remove unused fake_video_renderers from + // transceivers that have either stopped or are no longer receiving. + void RemoveUnusedVideoRenderers() { + auto transceivers = pc()->GetTransceivers(); + for (auto& transceiver : transceivers) { + if (transceiver->receiver()->media_type() != cricket::MEDIA_TYPE_VIDEO) { + continue; + } + // Remove fake video renderers from any stopped transceivers. + if (transceiver->stopped()) { + auto it = + fake_video_renderers_.find(transceiver->receiver()->track()->id()); + if (it != fake_video_renderers_.end()) { + fake_video_renderers_.erase(it); + } + } + // Remove fake video renderers from any transceivers that are no longer + // receiving. + if ((transceiver->current_direction() && + !webrtc::RtpTransceiverDirectionHasRecv( + *transceiver->current_direction()))) { + auto it = + fake_video_renderers_.find(transceiver->receiver()->track()->id()); + if (it != fake_video_renderers_.end()) { + fake_video_renderers_.erase(it); + } + } + } + } + // Simulate sending a blob of SDP with delay |signaling_delay_ms_| (0 by // default). void SendSdpMessage(SdpType type, const std::string& msg) { @@ -925,9 +942,11 @@ class PeerConnectionWrapper : public webrtc::PeerConnectionObserver, // |local_video_renderer_| attached to the first created local video track. std::unique_ptr local_video_renderer_; + SdpSemantics sdp_semantics_; PeerConnectionInterface::RTCOfferAnswerOptions offer_answer_options_; std::function received_sdp_munger_; std::function generated_sdp_munger_; + std::function remote_offer_handler_; rtc::scoped_refptr data_channel_; std::unique_ptr data_observer_; @@ -941,7 +960,7 @@ class PeerConnectionWrapper : public webrtc::PeerConnectionObserver, rtc::AsyncInvoker invoker_; - friend class PeerConnectionIntegrationTest; + friend class PeerConnectionIntegrationBaseTest; }; class MockRtcEventLogOutput : public webrtc::RtcEventLogOutput { @@ -951,14 +970,120 @@ class MockRtcEventLogOutput : public webrtc::RtcEventLogOutput { MOCK_METHOD1(Write, bool(const std::string&)); }; +// This helper object is used for both specifying how many audio/video frames +// are expected to be received for a caller/callee. It provides helper functions +// to specify these expectations. The object initially starts in a state of no +// expectations. +class MediaExpectations { + public: + enum ExpectFrames { + kExpectSomeFrames, + kExpectNoFrames, + kNoExpectation, + }; + + void ExpectBidirectionalAudioAndVideo() { + ExpectBidirectionalAudio(); + ExpectBidirectionalVideo(); + } + + void ExpectBidirectionalAudio() { + CallerExpectsSomeAudio(); + CalleeExpectsSomeAudio(); + } + + void ExpectNoAudio() { + CallerExpectsNoAudio(); + CalleeExpectsNoAudio(); + } + + void ExpectBidirectionalVideo() { + CallerExpectsSomeVideo(); + CalleeExpectsSomeVideo(); + } + + void ExpectNoVideo() { + CallerExpectsNoVideo(); + CalleeExpectsNoVideo(); + } + + void CallerExpectsSomeAudioAndVideo() { + CallerExpectsSomeAudio(); + CallerExpectsSomeVideo(); + } + + void CalleeExpectsSomeAudioAndVideo() { + CalleeExpectsSomeAudio(); + CalleeExpectsSomeVideo(); + } + + // Caller's audio functions. + void CallerExpectsSomeAudio( + int expected_audio_frames = kDefaultExpectedAudioFrameCount) { + caller_audio_expectation_ = kExpectSomeFrames; + caller_audio_frames_expected_ = expected_audio_frames; + } + + void CallerExpectsNoAudio() { + caller_audio_expectation_ = kExpectNoFrames; + caller_audio_frames_expected_ = 0; + } + + // Caller's video functions. + void CallerExpectsSomeVideo( + int expected_video_frames = kDefaultExpectedVideoFrameCount) { + caller_video_expectation_ = kExpectSomeFrames; + caller_video_frames_expected_ = expected_video_frames; + } + + void CallerExpectsNoVideo() { + caller_video_expectation_ = kExpectNoFrames; + caller_video_frames_expected_ = 0; + } + + // Callee's audio functions. + void CalleeExpectsSomeAudio( + int expected_audio_frames = kDefaultExpectedAudioFrameCount) { + callee_audio_expectation_ = kExpectSomeFrames; + callee_audio_frames_expected_ = expected_audio_frames; + } + + void CalleeExpectsNoAudio() { + callee_audio_expectation_ = kExpectNoFrames; + callee_audio_frames_expected_ = 0; + } + + // Callee's video functions. + void CalleeExpectsSomeVideo( + int expected_video_frames = kDefaultExpectedVideoFrameCount) { + callee_video_expectation_ = kExpectSomeFrames; + callee_video_frames_expected_ = expected_video_frames; + } + + void CalleeExpectsNoVideo() { + callee_video_expectation_ = kExpectNoFrames; + callee_video_frames_expected_ = 0; + } + + ExpectFrames caller_audio_expectation_ = kNoExpectation; + ExpectFrames caller_video_expectation_ = kNoExpectation; + ExpectFrames callee_audio_expectation_ = kNoExpectation; + ExpectFrames callee_video_expectation_ = kNoExpectation; + int caller_audio_frames_expected_ = 0; + int caller_video_frames_expected_ = 0; + int callee_audio_frames_expected_ = 0; + int callee_video_frames_expected_ = 0; +}; + // Tests two PeerConnections connecting to each other end-to-end, using a // virtual network, fake A/V capture and fake encoder/decoders. The // PeerConnections share the threads/socket servers, but use separate versions // of everything else (including "PeerConnectionFactory"s). -class PeerConnectionIntegrationTest : public testing::Test { +class PeerConnectionIntegrationBaseTest : public testing::Test { public: - PeerConnectionIntegrationTest() - : ss_(new rtc::VirtualSocketServer()), + explicit PeerConnectionIntegrationBaseTest(SdpSemantics sdp_semantics) + : sdp_semantics_(sdp_semantics), + ss_(new rtc::VirtualSocketServer()), fss_(new rtc::FirewallSocketServer(ss_.get())), network_thread_(new rtc::Thread(fss_.get())), worker_thread_(rtc::Thread::Create()) { @@ -966,7 +1091,7 @@ class PeerConnectionIntegrationTest : public testing::Test { RTC_CHECK(worker_thread_->Start()); } - ~PeerConnectionIntegrationTest() { + ~PeerConnectionIntegrationBaseTest() { if (caller_) { caller_->set_signaling_message_receiver(nullptr); } @@ -993,6 +1118,32 @@ class PeerConnectionIntegrationTest : public testing::Test { webrtc::PeerConnectionInterface::kIceConnectionCompleted); } + std::unique_ptr CreatePeerConnectionWrapper( + const std::string& debug_name, + const MediaConstraintsInterface* constraints, + const PeerConnectionFactory::Options* options, + const RTCConfiguration* config, + std::unique_ptr cert_generator) { + RTCConfiguration modified_config; + if (config) { + modified_config = *config; + } + if (modified_config.sdp_semantics == SdpSemantics::kDefault) { + modified_config.sdp_semantics = sdp_semantics_; + } + if (!cert_generator) { + cert_generator = rtc::MakeUnique(); + } + std::unique_ptr client( + new PeerConnectionWrapper(debug_name)); + if (!client->Init(constraints, options, &modified_config, + std::move(cert_generator), network_thread_.get(), + worker_thread_.get())) { + return nullptr; + } + return client; + } + bool CreatePeerConnectionWrappers() { return CreatePeerConnectionWrappersWithConfig( PeerConnectionInterface::RTCConfiguration(), @@ -1002,44 +1153,41 @@ class PeerConnectionIntegrationTest : public testing::Test { bool CreatePeerConnectionWrappersWithConstraints( MediaConstraintsInterface* caller_constraints, MediaConstraintsInterface* callee_constraints) { - caller_.reset(PeerConnectionWrapper::CreateWithConstraints( - "Caller", caller_constraints, network_thread_.get(), - worker_thread_.get())); - callee_.reset(PeerConnectionWrapper::CreateWithConstraints( - "Callee", callee_constraints, network_thread_.get(), - worker_thread_.get())); + caller_ = CreatePeerConnectionWrapper("Caller", caller_constraints, nullptr, + nullptr, nullptr); + callee_ = CreatePeerConnectionWrapper("Callee", callee_constraints, nullptr, + nullptr, nullptr); return caller_ && callee_; } bool CreatePeerConnectionWrappersWithConfig( const PeerConnectionInterface::RTCConfiguration& caller_config, const PeerConnectionInterface::RTCConfiguration& callee_config) { - caller_.reset(PeerConnectionWrapper::CreateWithConfig( - "Caller", caller_config, network_thread_.get(), worker_thread_.get())); - callee_.reset(PeerConnectionWrapper::CreateWithConfig( - "Callee", callee_config, network_thread_.get(), worker_thread_.get())); + caller_ = CreatePeerConnectionWrapper("Caller", nullptr, nullptr, + &caller_config, nullptr); + callee_ = CreatePeerConnectionWrapper("Callee", nullptr, nullptr, + &callee_config, nullptr); return caller_ && callee_; } bool CreatePeerConnectionWrappersWithOptions( const PeerConnectionFactory::Options& caller_options, const PeerConnectionFactory::Options& callee_options) { - caller_.reset(PeerConnectionWrapper::CreateWithOptions( - "Caller", caller_options, network_thread_.get(), worker_thread_.get())); - callee_.reset(PeerConnectionWrapper::CreateWithOptions( - "Callee", callee_options, network_thread_.get(), worker_thread_.get())); + caller_ = CreatePeerConnectionWrapper("Caller", nullptr, &caller_options, + nullptr, nullptr); + callee_ = CreatePeerConnectionWrapper("Callee", nullptr, &callee_options, + nullptr, nullptr); return caller_ && callee_; } - PeerConnectionWrapper* CreatePeerConnectionWrapperWithAlternateKey() { + std::unique_ptr + CreatePeerConnectionWrapperWithAlternateKey() { std::unique_ptr cert_generator( new FakeRTCCertificateGenerator()); cert_generator->use_alternate_key(); - // Make sure the new client is using a different certificate. - return PeerConnectionWrapper::CreateWithDtlsIdentityStore( - "New Peer", std::move(cert_generator), network_thread_.get(), - worker_thread_.get()); + return CreatePeerConnectionWrapper("New Peer", nullptr, nullptr, nullptr, + std::move(cert_generator)); } // Once called, SDP blobs and ICE candidates will be automatically signaled @@ -1110,46 +1258,109 @@ class PeerConnectionIntegrationTest : public testing::Test { rtc::FirewallSocketServer* firewall() const { return fss_.get(); } - // Expects the provided number of new frames to be received within |wait_ms|. - // "New frames" meaning that it waits for the current frame counts to - // *increase* by the provided values. For video, uses - // RecievedVideoFramesForEachTrack for the case of multiple video tracks - // being received. - void ExpectNewFramesReceivedWithWait( - int expected_caller_received_audio_frames, - int expected_caller_received_video_frames, - int expected_callee_received_audio_frames, - int expected_callee_received_video_frames, - int wait_ms) { - // Add current frame counts to the provided values, in order to wait for - // the frame count to increase. - expected_caller_received_audio_frames += caller()->audio_frames_received(); - expected_caller_received_video_frames += + // Expects the provided number of new frames to be received within + // kMaxWaitForFramesMs. The new expected frames are specified in + // |media_expectations|. Returns false if any of the expectations were + // not met. + bool ExpectNewFrames(const MediaExpectations& media_expectations) { + // First initialize the expected frame counts based upon the current + // frame count. + int total_caller_audio_frames_expected = caller()->audio_frames_received(); + if (media_expectations.caller_audio_expectation_ == + MediaExpectations::kExpectSomeFrames) { + total_caller_audio_frames_expected += + media_expectations.caller_audio_frames_expected_; + } + int total_caller_video_frames_expected = caller()->min_video_frames_received_per_track(); - expected_callee_received_audio_frames += callee()->audio_frames_received(); - expected_callee_received_video_frames += + if (media_expectations.caller_video_expectation_ == + MediaExpectations::kExpectSomeFrames) { + total_caller_video_frames_expected += + media_expectations.caller_video_frames_expected_; + } + int total_callee_audio_frames_expected = callee()->audio_frames_received(); + if (media_expectations.callee_audio_expectation_ == + MediaExpectations::kExpectSomeFrames) { + total_callee_audio_frames_expected += + media_expectations.callee_audio_frames_expected_; + } + int total_callee_video_frames_expected = callee()->min_video_frames_received_per_track(); + if (media_expectations.callee_video_expectation_ == + MediaExpectations::kExpectSomeFrames) { + total_callee_video_frames_expected += + media_expectations.callee_video_frames_expected_; + } + // Wait for the expected frames. EXPECT_TRUE_WAIT(caller()->audio_frames_received() >= - expected_caller_received_audio_frames && + total_caller_audio_frames_expected && caller()->min_video_frames_received_per_track() >= - expected_caller_received_video_frames && + total_caller_video_frames_expected && callee()->audio_frames_received() >= - expected_callee_received_audio_frames && + total_callee_audio_frames_expected && callee()->min_video_frames_received_per_track() >= - expected_callee_received_video_frames, - wait_ms); + total_callee_video_frames_expected, + kMaxWaitForFramesMs); + bool expectations_correct = + caller()->audio_frames_received() >= + total_caller_audio_frames_expected && + caller()->min_video_frames_received_per_track() >= + total_caller_video_frames_expected && + callee()->audio_frames_received() >= + total_callee_audio_frames_expected && + callee()->min_video_frames_received_per_track() >= + total_callee_video_frames_expected; - // After the combined wait, do an "expect" for each individual count, to - // print out a more detailed message upon failure. + // After the combined wait, print out a more detailed message upon + // failure. EXPECT_GE(caller()->audio_frames_received(), - expected_caller_received_audio_frames); + total_caller_audio_frames_expected); EXPECT_GE(caller()->min_video_frames_received_per_track(), - expected_caller_received_video_frames); + total_caller_video_frames_expected); EXPECT_GE(callee()->audio_frames_received(), - expected_callee_received_audio_frames); + total_callee_audio_frames_expected); EXPECT_GE(callee()->min_video_frames_received_per_track(), - expected_callee_received_video_frames); + total_callee_video_frames_expected); + + // We want to make sure nothing unexpected was received. + if (media_expectations.caller_audio_expectation_ == + MediaExpectations::kExpectNoFrames) { + EXPECT_EQ(caller()->audio_frames_received(), + total_caller_audio_frames_expected); + if (caller()->audio_frames_received() != + total_caller_audio_frames_expected) { + expectations_correct = false; + } + } + if (media_expectations.caller_video_expectation_ == + MediaExpectations::kExpectNoFrames) { + EXPECT_EQ(caller()->min_video_frames_received_per_track(), + total_caller_video_frames_expected); + if (caller()->min_video_frames_received_per_track() != + total_caller_video_frames_expected) { + expectations_correct = false; + } + } + if (media_expectations.callee_audio_expectation_ == + MediaExpectations::kExpectNoFrames) { + EXPECT_EQ(callee()->audio_frames_received(), + total_callee_audio_frames_expected); + if (callee()->audio_frames_received() != + total_callee_audio_frames_expected) { + expectations_correct = false; + } + } + if (media_expectations.callee_video_expectation_ == + MediaExpectations::kExpectNoFrames) { + EXPECT_EQ(callee()->min_video_frames_received_per_track(), + total_callee_video_frames_expected); + if (callee()->min_video_frames_received_per_track() != + total_callee_video_frames_expected) { + expectations_correct = false; + } + } + return expectations_correct; } void TestGcmNegotiationUsesCipherSuite(bool local_gcm_enabled, @@ -1177,6 +1388,9 @@ class PeerConnectionIntegrationTest : public testing::Test { caller()->pc()->RegisterUMAObserver(nullptr); } + protected: + const SdpSemantics sdp_semantics_; + private: // |ss_| is used by |network_thread_| so it must be destroyed later. std::unique_ptr ss_; @@ -1190,10 +1404,32 @@ class PeerConnectionIntegrationTest : public testing::Test { std::unique_ptr callee_; }; +class PeerConnectionIntegrationTest + : public PeerConnectionIntegrationBaseTest, + public ::testing::WithParamInterface { + protected: + PeerConnectionIntegrationTest() + : PeerConnectionIntegrationBaseTest(GetParam()) {} +}; + +class PeerConnectionIntegrationTestPlanB + : public PeerConnectionIntegrationBaseTest { + protected: + PeerConnectionIntegrationTestPlanB() + : PeerConnectionIntegrationBaseTest(SdpSemantics::kPlanB) {} +}; + +class PeerConnectionIntegrationTestUnifiedPlan + : public PeerConnectionIntegrationBaseTest { + protected: + PeerConnectionIntegrationTestUnifiedPlan() + : PeerConnectionIntegrationBaseTest(SdpSemantics::kUnifiedPlan) {} +}; + // Test the OnFirstPacketReceived callback from audio/video RtpReceivers. This // includes testing that the callback is invoked if an observer is connected // after the first packet has already been received. -TEST_F(PeerConnectionIntegrationTest, +TEST_P(PeerConnectionIntegrationTest, RtpReceiverObserverOnFirstPacketReceived) { ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); @@ -1284,7 +1520,7 @@ void TestDtmfFromSenderToReceiver(PeerConnectionWrapper* sender, // Verifies the DtmfSenderObserver callbacks for a DtmfSender (one in each // direction). -TEST_F(PeerConnectionIntegrationTest, DtmfSenderObserver) { +TEST_P(PeerConnectionIntegrationTest, DtmfSenderObserver) { ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); // Only need audio for DTMF. @@ -1300,7 +1536,7 @@ TEST_F(PeerConnectionIntegrationTest, DtmfSenderObserver) { // Basic end-to-end test, verifying media can be encoded/transmitted/decoded // between two connections, using DTLS-SRTP. -TEST_F(PeerConnectionIntegrationTest, EndToEndCallWithDtls) { +TEST_P(PeerConnectionIntegrationTest, EndToEndCallWithDtls) { ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); rtc::scoped_refptr caller_observer = @@ -1313,10 +1549,9 @@ TEST_F(PeerConnectionIntegrationTest, EndToEndCallWithDtls) { callee()->AddAudioVideoTracks(); caller()->CreateAndSetAndSignalOffer(); ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); - ExpectNewFramesReceivedWithWait( - kDefaultExpectedAudioFrameCount, kDefaultExpectedVideoFrameCount, - kDefaultExpectedAudioFrameCount, kDefaultExpectedVideoFrameCount, - kMaxWaitForFramesMs); + MediaExpectations media_expectations; + media_expectations.ExpectBidirectionalAudioAndVideo(); + ASSERT_TRUE(ExpectNewFrames(media_expectations)); EXPECT_LE( 1, caller_observer->GetEnumCounter(webrtc::kEnumCounterKeyProtocol, webrtc::kEnumCounterKeyProtocolDtls)); @@ -1326,7 +1561,7 @@ TEST_F(PeerConnectionIntegrationTest, EndToEndCallWithDtls) { } // Uses SDES instead of DTLS for key agreement. -TEST_F(PeerConnectionIntegrationTest, EndToEndCallWithSdes) { +TEST_P(PeerConnectionIntegrationTest, EndToEndCallWithSdes) { PeerConnectionInterface::RTCConfiguration sdes_config; sdes_config.enable_dtls_srtp.emplace(false); ASSERT_TRUE(CreatePeerConnectionWrappersWithConfig(sdes_config, sdes_config)); @@ -1341,10 +1576,9 @@ TEST_F(PeerConnectionIntegrationTest, EndToEndCallWithSdes) { callee()->AddAudioVideoTracks(); caller()->CreateAndSetAndSignalOffer(); ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); - ExpectNewFramesReceivedWithWait( - kDefaultExpectedAudioFrameCount, kDefaultExpectedVideoFrameCount, - kDefaultExpectedAudioFrameCount, kDefaultExpectedVideoFrameCount, - kMaxWaitForFramesMs); + MediaExpectations media_expectations; + media_expectations.ExpectBidirectionalAudioAndVideo(); + ASSERT_TRUE(ExpectNewFrames(media_expectations)); EXPECT_LE( 1, caller_observer->GetEnumCounter(webrtc::kEnumCounterKeyProtocol, webrtc::kEnumCounterKeyProtocolSdes)); @@ -1355,8 +1589,12 @@ TEST_F(PeerConnectionIntegrationTest, EndToEndCallWithSdes) { // Tests that the GetRemoteAudioSSLCertificate method returns the remote DTLS // certificate once the DTLS handshake has finished. -TEST_F(PeerConnectionIntegrationTest, +TEST_P(PeerConnectionIntegrationTest, GetRemoteAudioSSLCertificateReturnsExchangedCertificate) { + // TODO(bugs.webrtc.org/8764): Enable for Unified Plan once stats work. + if (sdp_semantics_ == SdpSemantics::kUnifiedPlan) { + return; + } auto GetRemoteAudioSSLCertificate = [](PeerConnectionWrapper* wrapper) { auto pci = reinterpret_cast(wrapper->pc()); auto pc = reinterpret_cast(pci->internal()); @@ -1425,7 +1663,7 @@ TEST_F(PeerConnectionIntegrationTest, // This test sets up a call between two parties (using DTLS) and tests that we // can get a video aspect ratio of 16:9. -TEST_F(PeerConnectionIntegrationTest, SendAndReceive16To9AspectRatio) { +TEST_P(PeerConnectionIntegrationTest, SendAndReceive16To9AspectRatio) { ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); @@ -1454,7 +1692,7 @@ TEST_F(PeerConnectionIntegrationTest, SendAndReceive16To9AspectRatio) { // This test sets up a call between two parties with a source resolution of // 1280x720 and verifies that a 16:9 aspect ratio is received. -TEST_F(PeerConnectionIntegrationTest, +TEST_P(PeerConnectionIntegrationTest, Send1280By720ResolutionAndReceive16To9AspectRatio) { ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); @@ -1485,60 +1723,86 @@ TEST_F(PeerConnectionIntegrationTest, // This test sets up an one-way call, with media only from caller to // callee. -TEST_F(PeerConnectionIntegrationTest, OneWayMediaCall) { +TEST_P(PeerConnectionIntegrationTest, OneWayMediaCall) { ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); caller()->AddAudioVideoTracks(); caller()->CreateAndSetAndSignalOffer(); - int caller_received_frames = 0; - ExpectNewFramesReceivedWithWait( - caller_received_frames, caller_received_frames, - kDefaultExpectedAudioFrameCount, kDefaultExpectedVideoFrameCount, - kMaxWaitForFramesMs); + MediaExpectations media_expectations; + media_expectations.CalleeExpectsSomeAudioAndVideo(); + media_expectations.CallerExpectsNoAudio(); + media_expectations.CallerExpectsNoVideo(); + ASSERT_TRUE(ExpectNewFrames(media_expectations)); } // This test sets up a audio call initially, with the callee rejecting video // initially. Then later the callee decides to upgrade to audio/video, and // initiates a new offer/answer exchange. -TEST_F(PeerConnectionIntegrationTest, AudioToVideoUpgrade) { +TEST_P(PeerConnectionIntegrationTest, AudioToVideoUpgrade) { ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); // Initially, offer an audio/video stream from the caller, but refuse to // send/receive video on the callee side. caller()->AddAudioVideoTracks(); callee()->AddAudioTrack(); - PeerConnectionInterface::RTCOfferAnswerOptions options; - options.offer_to_receive_video = 0; - callee()->SetOfferAnswerOptions(options); + if (sdp_semantics_ == SdpSemantics::kPlanB) { + PeerConnectionInterface::RTCOfferAnswerOptions options; + options.offer_to_receive_video = 0; + callee()->SetOfferAnswerOptions(options); + } else { + callee()->SetRemoteOfferHandler([this] { + callee()->GetFirstTransceiverOfType(cricket::MEDIA_TYPE_VIDEO)->Stop(); + }); + } // Do offer/answer and make sure audio is still received end-to-end. caller()->CreateAndSetAndSignalOffer(); ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); - ExpectNewFramesReceivedWithWait(kDefaultExpectedAudioFrameCount, 0, - kDefaultExpectedAudioFrameCount, 0, - kMaxWaitForFramesMs); + { + MediaExpectations media_expectations; + media_expectations.ExpectBidirectionalAudio(); + media_expectations.ExpectNoVideo(); + ASSERT_TRUE(ExpectNewFrames(media_expectations)); + } // Sanity check that the callee's description has a rejected video section. ASSERT_NE(nullptr, callee()->pc()->local_description()); const ContentInfo* callee_video_content = GetFirstVideoContent(callee()->pc()->local_description()->description()); ASSERT_NE(nullptr, callee_video_content); EXPECT_TRUE(callee_video_content->rejected); + // Now negotiate with video and ensure negotiation succeeds, with video // frames and additional audio frames being received. callee()->AddVideoTrack(); - options.offer_to_receive_video = 1; - callee()->SetOfferAnswerOptions(options); + if (sdp_semantics_ == SdpSemantics::kPlanB) { + PeerConnectionInterface::RTCOfferAnswerOptions options; + options.offer_to_receive_video = 1; + callee()->SetOfferAnswerOptions(options); + } else { + callee()->SetRemoteOfferHandler(nullptr); + caller()->SetRemoteOfferHandler([this] { + // The caller creates a new transceiver to receive video on when receiving + // the offer, but by default it is send only. + auto transceivers = caller()->pc()->GetTransceivers(); + ASSERT_EQ(3, transceivers.size()); + ASSERT_EQ(cricket::MEDIA_TYPE_VIDEO, + transceivers[2]->receiver()->media_type()); + transceivers[2]->sender()->SetTrack(caller()->CreateLocalVideoTrack()); + transceivers[2]->SetDirection(RtpTransceiverDirection::kSendRecv); + }); + } callee()->CreateAndSetAndSignalOffer(); ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); - // Expect additional audio frames to be received after the upgrade. - ExpectNewFramesReceivedWithWait( - kDefaultExpectedAudioFrameCount, kDefaultExpectedVideoFrameCount, - kDefaultExpectedAudioFrameCount, kDefaultExpectedVideoFrameCount, - kMaxWaitForFramesMs); + { + // Expect additional audio frames to be received after the upgrade. + MediaExpectations media_expectations; + media_expectations.ExpectBidirectionalAudioAndVideo(); + ASSERT_TRUE(ExpectNewFrames(media_expectations)); + } } // Simpler than the above test; just add an audio track to an established // video-only connection. -TEST_F(PeerConnectionIntegrationTest, AddAudioToVideoOnlyCall) { +TEST_P(PeerConnectionIntegrationTest, AddAudioToVideoOnlyCall) { ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); // Do initial offer/answer with just a video track. @@ -1552,15 +1816,14 @@ TEST_F(PeerConnectionIntegrationTest, AddAudioToVideoOnlyCall) { caller()->CreateAndSetAndSignalOffer(); ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); // Ensure both audio and video frames are received end-to-end. - ExpectNewFramesReceivedWithWait( - kDefaultExpectedAudioFrameCount, kDefaultExpectedVideoFrameCount, - kDefaultExpectedAudioFrameCount, kDefaultExpectedVideoFrameCount, - kMaxWaitForFramesMs); + MediaExpectations media_expectations; + media_expectations.ExpectBidirectionalAudioAndVideo(); + ASSERT_TRUE(ExpectNewFrames(media_expectations)); } // This test sets up a call that's transferred to a new caller with a different // DTLS fingerprint. -TEST_F(PeerConnectionIntegrationTest, CallTransferredForCallee) { +TEST_P(PeerConnectionIntegrationTest, CallTransferredForCallee) { ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); caller()->AddAudioVideoTracks(); @@ -1572,7 +1835,7 @@ TEST_F(PeerConnectionIntegrationTest, CallTransferredForCallee) { // receiving client. These SRTP packets will be dropped. std::unique_ptr original_peer( SetCallerPcWrapperAndReturnCurrent( - CreatePeerConnectionWrapperWithAlternateKey())); + CreatePeerConnectionWrapperWithAlternateKey().release())); // TODO(deadbeef): Why do we call Close here? That goes against the comment // directly above. original_peer->pc()->Close(); @@ -1582,15 +1845,14 @@ TEST_F(PeerConnectionIntegrationTest, CallTransferredForCallee) { caller()->CreateAndSetAndSignalOffer(); ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); // Wait for some additional frames to be transmitted end-to-end. - ExpectNewFramesReceivedWithWait( - kDefaultExpectedAudioFrameCount, kDefaultExpectedVideoFrameCount, - kDefaultExpectedAudioFrameCount, kDefaultExpectedVideoFrameCount, - kMaxWaitForFramesMs); + MediaExpectations media_expectations; + media_expectations.ExpectBidirectionalAudioAndVideo(); + ASSERT_TRUE(ExpectNewFrames(media_expectations)); } // This test sets up a call that's transferred to a new callee with a different // DTLS fingerprint. -TEST_F(PeerConnectionIntegrationTest, CallTransferredForCaller) { +TEST_P(PeerConnectionIntegrationTest, CallTransferredForCaller) { ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); caller()->AddAudioVideoTracks(); @@ -1602,7 +1864,7 @@ TEST_F(PeerConnectionIntegrationTest, CallTransferredForCaller) { // receiving client. These SRTP packets will be dropped. std::unique_ptr original_peer( SetCalleePcWrapperAndReturnCurrent( - CreatePeerConnectionWrapperWithAlternateKey())); + CreatePeerConnectionWrapperWithAlternateKey().release())); // TODO(deadbeef): Why do we call Close here? That goes against the comment // directly above. original_peer->pc()->Close(); @@ -1613,16 +1875,15 @@ TEST_F(PeerConnectionIntegrationTest, CallTransferredForCaller) { caller()->CreateAndSetAndSignalOffer(); ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); // Wait for some additional frames to be transmitted end-to-end. - ExpectNewFramesReceivedWithWait( - kDefaultExpectedAudioFrameCount, kDefaultExpectedVideoFrameCount, - kDefaultExpectedAudioFrameCount, kDefaultExpectedVideoFrameCount, - kMaxWaitForFramesMs); + MediaExpectations media_expectations; + media_expectations.ExpectBidirectionalAudioAndVideo(); + ASSERT_TRUE(ExpectNewFrames(media_expectations)); } // This test sets up a non-bundled call and negotiates bundling at the same // time as starting an ICE restart. When bundling is in effect in the restart, // the DTLS-SRTP context should be successfully reset. -TEST_F(PeerConnectionIntegrationTest, BundlingEnabledWhileIceRestartOccurs) { +TEST_P(PeerConnectionIntegrationTest, BundlingEnabledWhileIceRestartOccurs) { ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); @@ -1634,11 +1895,11 @@ TEST_F(PeerConnectionIntegrationTest, BundlingEnabledWhileIceRestartOccurs) { }); caller()->CreateAndSetAndSignalOffer(); ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); - ExpectNewFramesReceivedWithWait( - kDefaultExpectedAudioFrameCount, kDefaultExpectedVideoFrameCount, - kDefaultExpectedAudioFrameCount, kDefaultExpectedVideoFrameCount, - kMaxWaitForFramesMs); - + { + MediaExpectations media_expectations; + media_expectations.ExpectBidirectionalAudioAndVideo(); + ASSERT_TRUE(ExpectNewFrames(media_expectations)); + } // Now stop removing the BUNDLE group, and trigger an ICE restart. callee()->SetReceivedSdpMunger(nullptr); caller()->SetOfferAnswerOptions(IceRestartOfferAnswerOptions()); @@ -1646,17 +1907,18 @@ TEST_F(PeerConnectionIntegrationTest, BundlingEnabledWhileIceRestartOccurs) { ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); // Expect additional frames to be received after the ICE restart. - ExpectNewFramesReceivedWithWait( - kDefaultExpectedAudioFrameCount, kDefaultExpectedVideoFrameCount, - kDefaultExpectedAudioFrameCount, kDefaultExpectedVideoFrameCount, - kMaxWaitForFramesMs); + { + MediaExpectations media_expectations; + media_expectations.ExpectBidirectionalAudioAndVideo(); + ASSERT_TRUE(ExpectNewFrames(media_expectations)); + } } // Test CVO (Coordination of Video Orientation). If a video source is rotated // and both peers support the CVO RTP header extension, the actual video frames // don't need to be encoded in different resolutions, since the rotation is // communicated through the RTP header extension. -TEST_F(PeerConnectionIntegrationTest, RotatedVideoWithCVOExtension) { +TEST_P(PeerConnectionIntegrationTest, RotatedVideoWithCVOExtension) { ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); // Add rotated video tracks. @@ -1686,7 +1948,7 @@ TEST_F(PeerConnectionIntegrationTest, RotatedVideoWithCVOExtension) { // Test that when the CVO extension isn't supported, video is rotated the // old-fashioned way, by encoding rotated frames. -TEST_F(PeerConnectionIntegrationTest, RotatedVideoWithoutCVOExtension) { +TEST_P(PeerConnectionIntegrationTest, RotatedVideoWithoutCVOExtension) { ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); // Add rotated video tracks. @@ -1721,66 +1983,88 @@ TEST_F(PeerConnectionIntegrationTest, RotatedVideoWithoutCVOExtension) { EXPECT_EQ(webrtc::kVideoRotation_0, callee()->rendered_rotation()); } -// TODO(deadbeef): The tests below rely on RTCOfferAnswerOptions to reject an -// m= section. When we implement Unified Plan SDP, the right way to do this -// would be by stopping an RtpTransceiver. - // Test that if the answerer rejects the audio m= section, no audio is sent or // received, but video still can be. -TEST_F(PeerConnectionIntegrationTest, AnswererRejectsAudioSection) { +TEST_P(PeerConnectionIntegrationTest, AnswererRejectsAudioSection) { ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); caller()->AddAudioVideoTracks(); - // Only add video track for callee, and set offer_to_receive_audio to 0, so - // it will reject the audio m= section completely. - PeerConnectionInterface::RTCOfferAnswerOptions options; - options.offer_to_receive_audio = 0; - callee()->SetOfferAnswerOptions(options); + if (sdp_semantics_ == SdpSemantics::kPlanB) { + // Only add video track for callee, and set offer_to_receive_audio to 0, so + // it will reject the audio m= section completely. + PeerConnectionInterface::RTCOfferAnswerOptions options; + options.offer_to_receive_audio = 0; + callee()->SetOfferAnswerOptions(options); + } else { + // Stopping the audio RtpTransceiver will cause the media section to be + // rejected in the answer. + callee()->SetRemoteOfferHandler([this] { + callee()->GetFirstTransceiverOfType(cricket::MEDIA_TYPE_AUDIO)->Stop(); + }); + } callee()->AddTrack(callee()->CreateLocalVideoTrack()); // Do offer/answer and wait for successful end-to-end video frames. caller()->CreateAndSetAndSignalOffer(); ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); - ExpectNewFramesReceivedWithWait(0, kDefaultExpectedVideoFrameCount, 0, - kDefaultExpectedVideoFrameCount, - kMaxWaitForFramesMs); - // Shouldn't have received audio frames at any point. - EXPECT_EQ(0, caller()->audio_frames_received()); - EXPECT_EQ(0, callee()->audio_frames_received()); + MediaExpectations media_expectations; + media_expectations.ExpectBidirectionalVideo(); + media_expectations.ExpectNoAudio(); + ASSERT_TRUE(ExpectNewFrames(media_expectations)); + // Sanity check that the callee's description has a rejected audio section. ASSERT_NE(nullptr, callee()->pc()->local_description()); const ContentInfo* callee_audio_content = GetFirstAudioContent(callee()->pc()->local_description()->description()); ASSERT_NE(nullptr, callee_audio_content); EXPECT_TRUE(callee_audio_content->rejected); + if (sdp_semantics_ == SdpSemantics::kUnifiedPlan) { + // The caller's transceiver should have stopped after receiving the answer. + EXPECT_TRUE(caller() + ->GetFirstTransceiverOfType(cricket::MEDIA_TYPE_AUDIO) + ->stopped()); + } } // Test that if the answerer rejects the video m= section, no video is sent or // received, but audio still can be. -TEST_F(PeerConnectionIntegrationTest, AnswererRejectsVideoSection) { +TEST_P(PeerConnectionIntegrationTest, AnswererRejectsVideoSection) { ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); caller()->AddAudioVideoTracks(); - // Only add audio track for callee, and set offer_to_receive_video to 0, so - // it will reject the video m= section completely. - PeerConnectionInterface::RTCOfferAnswerOptions options; - options.offer_to_receive_video = 0; - callee()->SetOfferAnswerOptions(options); + if (sdp_semantics_ == SdpSemantics::kPlanB) { + // Only add audio track for callee, and set offer_to_receive_video to 0, so + // it will reject the video m= section completely. + PeerConnectionInterface::RTCOfferAnswerOptions options; + options.offer_to_receive_video = 0; + callee()->SetOfferAnswerOptions(options); + } else { + // Stopping the video RtpTransceiver will cause the media section to be + // rejected in the answer. + callee()->SetRemoteOfferHandler([this] { + callee()->GetFirstTransceiverOfType(cricket::MEDIA_TYPE_VIDEO)->Stop(); + }); + } callee()->AddTrack(callee()->CreateLocalAudioTrack()); // Do offer/answer and wait for successful end-to-end audio frames. caller()->CreateAndSetAndSignalOffer(); ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); - ExpectNewFramesReceivedWithWait(kDefaultExpectedAudioFrameCount, 0, - kDefaultExpectedAudioFrameCount, 0, - kMaxWaitForFramesMs); - // Shouldn't have received video frames at any point. - EXPECT_EQ(0, caller()->total_video_frames_received()); - EXPECT_EQ(0, callee()->total_video_frames_received()); + MediaExpectations media_expectations; + media_expectations.ExpectBidirectionalAudio(); + media_expectations.ExpectNoVideo(); + ASSERT_TRUE(ExpectNewFrames(media_expectations)); + // Sanity check that the callee's description has a rejected video section. ASSERT_NE(nullptr, callee()->pc()->local_description()); const ContentInfo* callee_video_content = GetFirstVideoContent(callee()->pc()->local_description()->description()); ASSERT_NE(nullptr, callee_video_content); EXPECT_TRUE(callee_video_content->rejected); + if (sdp_semantics_ == SdpSemantics::kUnifiedPlan) { + // The caller's transceiver should have stopped after receiving the answer. + EXPECT_TRUE(caller() + ->GetFirstTransceiverOfType(cricket::MEDIA_TYPE_VIDEO) + ->stopped()); + } } // Test that if the answerer rejects both audio and video m= sections, nothing @@ -1788,19 +2072,29 @@ TEST_F(PeerConnectionIntegrationTest, AnswererRejectsVideoSection) { // TODO(deadbeef): Test that a data channel still works. Currently this doesn't // test anything but the fact that negotiation succeeds, which doesn't mean // much. -TEST_F(PeerConnectionIntegrationTest, AnswererRejectsAudioAndVideoSections) { +TEST_P(PeerConnectionIntegrationTest, AnswererRejectsAudioAndVideoSections) { ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); caller()->AddAudioVideoTracks(); - // Don't give the callee any tracks, and set offer_to_receive_X to 0, so it - // will reject both audio and video m= sections. - PeerConnectionInterface::RTCOfferAnswerOptions options; - options.offer_to_receive_audio = 0; - options.offer_to_receive_video = 0; - callee()->SetOfferAnswerOptions(options); + if (sdp_semantics_ == SdpSemantics::kPlanB) { + // Don't give the callee any tracks, and set offer_to_receive_X to 0, so it + // will reject both audio and video m= sections. + PeerConnectionInterface::RTCOfferAnswerOptions options; + options.offer_to_receive_audio = 0; + options.offer_to_receive_video = 0; + callee()->SetOfferAnswerOptions(options); + } else { + callee()->SetRemoteOfferHandler([this] { + // Stopping all transceivers will cause all media sections to be rejected. + for (auto transceiver : callee()->pc()->GetTransceivers()) { + transceiver->Stop(); + } + }); + } // Do offer/answer and wait for stable signaling state. caller()->CreateAndSetAndSignalOffer(); ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); + // Sanity check that the callee's description has rejected m= sections. ASSERT_NE(nullptr, callee()->pc()->local_description()); const ContentInfo* callee_audio_content = @@ -1817,28 +2111,31 @@ TEST_F(PeerConnectionIntegrationTest, AnswererRejectsAudioAndVideoSections) { // call runs for a while, the caller sends an updated offer with video being // rejected. Once the re-negotiation is done, the video flow should stop and // the audio flow should continue. -TEST_F(PeerConnectionIntegrationTest, VideoRejectedInSubsequentOffer) { +TEST_P(PeerConnectionIntegrationTest, VideoRejectedInSubsequentOffer) { ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); caller()->AddAudioVideoTracks(); callee()->AddAudioVideoTracks(); caller()->CreateAndSetAndSignalOffer(); ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); - ExpectNewFramesReceivedWithWait( - kDefaultExpectedAudioFrameCount, kDefaultExpectedVideoFrameCount, - kDefaultExpectedAudioFrameCount, kDefaultExpectedVideoFrameCount, - kMaxWaitForFramesMs); - + { + MediaExpectations media_expectations; + media_expectations.ExpectBidirectionalAudioAndVideo(); + ASSERT_TRUE(ExpectNewFrames(media_expectations)); + } // Renegotiate, rejecting the video m= section. - // TODO(deadbeef): When an RtpTransceiver API is available, use that to - // reject the video m= section. - caller()->SetGeneratedSdpMunger([](cricket::SessionDescription* description) { - for (cricket::ContentInfo& content : description->contents()) { - if (cricket::IsVideoContent(&content)) { - content.rejected = true; - } - } - }); + if (sdp_semantics_ == SdpSemantics::kPlanB) { + caller()->SetGeneratedSdpMunger( + [](cricket::SessionDescription* description) { + for (cricket::ContentInfo& content : description->contents()) { + if (cricket::IsVideoContent(&content)) { + content.rejected = true; + } + } + }); + } else { + caller()->GetFirstTransceiverOfType(cricket::MEDIA_TYPE_VIDEO)->Stop(); + } caller()->CreateAndSetAndSignalOffer(); ASSERT_TRUE_WAIT(SignalingStateStable(), kMaxWaitForActivationMs); @@ -1848,19 +2145,13 @@ TEST_F(PeerConnectionIntegrationTest, VideoRejectedInSubsequentOffer) { GetFirstVideoContent(caller()->pc()->local_description()->description()); ASSERT_NE(nullptr, caller_video_content); EXPECT_TRUE(caller_video_content->rejected); - - int caller_video_received = caller()->total_video_frames_received(); - int callee_video_received = callee()->total_video_frames_received(); - // Wait for some additional audio frames to be received. - ExpectNewFramesReceivedWithWait(kDefaultExpectedAudioFrameCount, 0, - kDefaultExpectedAudioFrameCount, 0, - kMaxWaitForFramesMs); - - // During this time, we shouldn't have received any additional video frames - // for the rejected video tracks. - EXPECT_EQ(caller_video_received, caller()->total_video_frames_received()); - EXPECT_EQ(callee_video_received, callee()->total_video_frames_received()); + { + MediaExpectations media_expectations; + media_expectations.ExpectBidirectionalAudio(); + media_expectations.ExpectNoVideo(); + ASSERT_TRUE(ExpectNewFrames(media_expectations)); + } } // Basic end-to-end test, but without SSRC/MSID signaling. This functionality @@ -1868,7 +2159,7 @@ TEST_F(PeerConnectionIntegrationTest, VideoRejectedInSubsequentOffer) { // TODO(deadbeef): When we support the MID extension and demuxing on MID, also // add a test for an end-to-end test without MID signaling either (basically, // the minimum acceptable SDP). -TEST_F(PeerConnectionIntegrationTest, EndToEndCallWithoutSsrcOrMsidSignaling) { +TEST_P(PeerConnectionIntegrationTest, EndToEndCallWithoutSsrcOrMsidSignaling) { ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); // Add audio and video, testing that packets can be demuxed on payload type. @@ -1878,15 +2169,14 @@ TEST_F(PeerConnectionIntegrationTest, EndToEndCallWithoutSsrcOrMsidSignaling) { callee()->SetReceivedSdpMunger(RemoveSsrcsAndMsids); caller()->CreateAndSetAndSignalOffer(); ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); - ExpectNewFramesReceivedWithWait( - kDefaultExpectedAudioFrameCount, kDefaultExpectedVideoFrameCount, - kDefaultExpectedAudioFrameCount, kDefaultExpectedVideoFrameCount, - kMaxWaitForFramesMs); + MediaExpectations media_expectations; + media_expectations.ExpectBidirectionalAudioAndVideo(); + ASSERT_TRUE(ExpectNewFrames(media_expectations)); } // Test that if two video tracks are sent (from caller to callee, in this test), // they're transmitted correctly end-to-end. -TEST_F(PeerConnectionIntegrationTest, EndToEndCallWithTwoVideoTracks) { +TEST_P(PeerConnectionIntegrationTest, EndToEndCallWithTwoVideoTracks) { ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); // Add one audio/video stream, and one video-only stream. @@ -1895,9 +2185,10 @@ TEST_F(PeerConnectionIntegrationTest, EndToEndCallWithTwoVideoTracks) { caller()->CreateAndSetAndSignalOffer(); ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); ASSERT_EQ(3u, callee()->pc()->GetReceivers().size()); - int expected_callee_received_frames = kDefaultExpectedVideoFrameCount; - ExpectNewFramesReceivedWithWait(0, 0, 0, expected_callee_received_frames, - kMaxWaitForFramesMs); + + MediaExpectations media_expectations; + media_expectations.CalleeExpectsSomeAudioAndVideo(); + ASSERT_TRUE(ExpectNewFrames(media_expectations)); } static void MakeSpecCompliantMaxBundleOffer(cricket::SessionDescription* desc) { @@ -1929,7 +2220,7 @@ static void MakeSpecCompliantMaxBundleOffer(cricket::SessionDescription* desc) { // TODO(deadbeef): Update this test to also omit "a=rtcp-mux", once that works. // TODO(deadbeef): Won't need this test once we start generating actual // standards-compliant SDP. -TEST_F(PeerConnectionIntegrationTest, +TEST_P(PeerConnectionIntegrationTest, EndToEndCallWithSpecCompliantMaxBundleOffer) { ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); @@ -1941,16 +2232,15 @@ TEST_F(PeerConnectionIntegrationTest, callee()->SetReceivedSdpMunger(MakeSpecCompliantMaxBundleOffer); caller()->CreateAndSetAndSignalOffer(); ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); - ExpectNewFramesReceivedWithWait( - kDefaultExpectedAudioFrameCount, kDefaultExpectedVideoFrameCount, - kDefaultExpectedAudioFrameCount, kDefaultExpectedVideoFrameCount, - kMaxWaitForFramesMs); + MediaExpectations media_expectations; + media_expectations.ExpectBidirectionalAudioAndVideo(); + ASSERT_TRUE(ExpectNewFrames(media_expectations)); } // Test that we can receive the audio output level from a remote audio track. // TODO(deadbeef): Use a fake audio source and verify that the output level is // exactly what the source on the other side was configured with. -TEST_F(PeerConnectionIntegrationTest, GetAudioOutputLevelStatsWithOldStatsApi) { +TEST_P(PeerConnectionIntegrationTest, GetAudioOutputLevelStatsWithOldStatsApi) { ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); // Just add an audio track. @@ -1967,7 +2257,7 @@ TEST_F(PeerConnectionIntegrationTest, GetAudioOutputLevelStatsWithOldStatsApi) { // Test that an audio input level is reported. // TODO(deadbeef): Use a fake audio source and verify that the input level is // exactly what the source was configured with. -TEST_F(PeerConnectionIntegrationTest, GetAudioInputLevelStatsWithOldStatsApi) { +TEST_P(PeerConnectionIntegrationTest, GetAudioInputLevelStatsWithOldStatsApi) { ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); // Just add an audio track. @@ -1982,18 +2272,17 @@ TEST_F(PeerConnectionIntegrationTest, GetAudioInputLevelStatsWithOldStatsApi) { } // Test that we can get incoming byte counts from both audio and video tracks. -TEST_F(PeerConnectionIntegrationTest, GetBytesReceivedStatsWithOldStatsApi) { +TEST_P(PeerConnectionIntegrationTest, GetBytesReceivedStatsWithOldStatsApi) { ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); caller()->AddAudioVideoTracks(); // Do offer/answer, wait for the callee to receive some frames. caller()->CreateAndSetAndSignalOffer(); ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); - int expected_caller_received_frames = 0; - ExpectNewFramesReceivedWithWait( - expected_caller_received_frames, expected_caller_received_frames, - kDefaultExpectedAudioFrameCount, kDefaultExpectedVideoFrameCount, - kMaxWaitForFramesMs); + + MediaExpectations media_expectations; + media_expectations.CalleeExpectsSomeAudioAndVideo(); + ASSERT_TRUE(ExpectNewFrames(media_expectations)); // Get a handle to the remote tracks created, so they can be used as GetStats // filters. @@ -2006,7 +2295,7 @@ TEST_F(PeerConnectionIntegrationTest, GetBytesReceivedStatsWithOldStatsApi) { } // Test that we can get outgoing byte counts from both audio and video tracks. -TEST_F(PeerConnectionIntegrationTest, GetBytesSentStatsWithOldStatsApi) { +TEST_P(PeerConnectionIntegrationTest, GetBytesSentStatsWithOldStatsApi) { ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); caller()->AddAudioVideoTracks(); @@ -2017,11 +2306,9 @@ TEST_F(PeerConnectionIntegrationTest, GetBytesSentStatsWithOldStatsApi) { // Do offer/answer, wait for the callee to receive some frames. caller()->CreateAndSetAndSignalOffer(); ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); - int expected_caller_received_frames = 0; - ExpectNewFramesReceivedWithWait( - expected_caller_received_frames, expected_caller_received_frames, - kDefaultExpectedAudioFrameCount, kDefaultExpectedVideoFrameCount, - kMaxWaitForFramesMs); + MediaExpectations media_expectations; + media_expectations.CalleeExpectsSomeAudioAndVideo(); + ASSERT_TRUE(ExpectNewFrames(media_expectations)); // The callee received frames, so we definitely should have nonzero "sent // bytes" stats at this point. @@ -2030,7 +2317,7 @@ TEST_F(PeerConnectionIntegrationTest, GetBytesSentStatsWithOldStatsApi) { } // Test that we can get capture start ntp time. -TEST_F(PeerConnectionIntegrationTest, GetCaptureStartNtpTimeWithOldStatsApi) { +TEST_P(PeerConnectionIntegrationTest, GetCaptureStartNtpTimeWithOldStatsApi) { ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); caller()->AddAudioTrack(); @@ -2058,7 +2345,7 @@ TEST_F(PeerConnectionIntegrationTest, GetCaptureStartNtpTimeWithOldStatsApi) { // Test that we can get stats (using the new stats implemnetation) for // unsignaled streams. Meaning when SSRCs/MSIDs aren't signaled explicitly in // SDP. -TEST_F(PeerConnectionIntegrationTest, +TEST_P(PeerConnectionIntegrationTest, GetStatsForUnsignaledStreamWithNewStatsApi) { ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); @@ -2067,8 +2354,9 @@ TEST_F(PeerConnectionIntegrationTest, callee()->SetReceivedSdpMunger(RemoveSsrcsAndMsids); caller()->CreateAndSetAndSignalOffer(); ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); - // Wait for one audio frame to be received by the callee. - ExpectNewFramesReceivedWithWait(0, 0, 1, 0, kMaxWaitForFramesMs); + MediaExpectations media_expectations; + media_expectations.CalleeExpectsSomeAudio(1); + ASSERT_TRUE(ExpectNewFrames(media_expectations)); // We received a frame, so we should have nonzero "bytes received" stats for // the unsignaled stream, if stats are working for it. @@ -2085,7 +2373,7 @@ TEST_F(PeerConnectionIntegrationTest, // Test that we can successfully get the media related stats (audio level // etc.) for the unsignaled stream. -TEST_F(PeerConnectionIntegrationTest, +TEST_P(PeerConnectionIntegrationTest, GetMediaStatsForUnsignaledStreamWithNewStatsApi) { ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); @@ -2094,8 +2382,10 @@ TEST_F(PeerConnectionIntegrationTest, callee()->SetReceivedSdpMunger(RemoveSsrcsAndMsids); caller()->CreateAndSetAndSignalOffer(); ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); - // Wait for one audio frame to be received by the callee. - ExpectNewFramesReceivedWithWait(0, 0, 1, 1, kMaxWaitForFramesMs); + MediaExpectations media_expectations; + media_expectations.CalleeExpectsSomeAudio(1); + media_expectations.CalleeExpectsSomeVideo(1); + ASSERT_TRUE(ExpectNewFrames(media_expectations)); rtc::scoped_refptr report = callee()->NewGetStats(); @@ -2134,7 +2424,7 @@ void ModifySsrcs(cricket::SessionDescription* desc) { // received, and a 50% chance that they'll stop updating (while // "concealed_samples" continues increasing, due to silence being generated for // the inactive stream). -TEST_F(PeerConnectionIntegrationTest, +TEST_P(PeerConnectionIntegrationTest, TrackStatsUpdatedCorrectlyWhenUnsignaledSsrcChanges) { ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); @@ -2145,8 +2435,11 @@ TEST_F(PeerConnectionIntegrationTest, caller()->CreateAndSetAndSignalOffer(); ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); // Wait for 50 audio frames (500ms of audio) to be received by the callee. - ExpectNewFramesReceivedWithWait(0, 0, 25, 0, kMaxWaitForFramesMs); - + { + MediaExpectations media_expectations; + media_expectations.CalleeExpectsSomeAudio(50); + ASSERT_TRUE(ExpectNewFrames(media_expectations)); + } // Some audio frames were received, so we should have nonzero "samples // received" for the track. rtc::scoped_refptr report = @@ -2164,7 +2457,11 @@ TEST_F(PeerConnectionIntegrationTest, ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); // Wait for 25 more audio frames (250ms of audio) to be received, from the new // SSRC. - ExpectNewFramesReceivedWithWait(0, 0, 25, 0, kMaxWaitForFramesMs); + { + MediaExpectations media_expectations; + media_expectations.CalleeExpectsSomeAudio(25); + ASSERT_TRUE(ExpectNewFrames(media_expectations)); + } report = callee()->NewGetStats(); ASSERT_NE(nullptr, report); @@ -2198,7 +2495,7 @@ TEST_F(PeerConnectionIntegrationTest, } // Test that DTLS 1.0 is used if both sides only support DTLS 1.0. -TEST_F(PeerConnectionIntegrationTest, EndToEndCallWithDtls10) { +TEST_P(PeerConnectionIntegrationTest, EndToEndCallWithDtls10) { PeerConnectionFactory::Options dtls_10_options; dtls_10_options.ssl_max_version = rtc::SSL_PROTOCOL_DTLS_10; ASSERT_TRUE(CreatePeerConnectionWrappersWithOptions(dtls_10_options, @@ -2210,14 +2507,13 @@ TEST_F(PeerConnectionIntegrationTest, EndToEndCallWithDtls10) { callee()->AddAudioVideoTracks(); caller()->CreateAndSetAndSignalOffer(); ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); - ExpectNewFramesReceivedWithWait( - kDefaultExpectedAudioFrameCount, kDefaultExpectedVideoFrameCount, - kDefaultExpectedAudioFrameCount, kDefaultExpectedVideoFrameCount, - kMaxWaitForFramesMs); + MediaExpectations media_expectations; + media_expectations.ExpectBidirectionalAudioAndVideo(); + ASSERT_TRUE(ExpectNewFrames(media_expectations)); } // Test getting cipher stats and UMA metrics when DTLS 1.0 is negotiated. -TEST_F(PeerConnectionIntegrationTest, Dtls10CipherStatsAndUmaMetrics) { +TEST_P(PeerConnectionIntegrationTest, Dtls10CipherStatsAndUmaMetrics) { PeerConnectionFactory::Options dtls_10_options; dtls_10_options.ssl_max_version = rtc::SSL_PROTOCOL_DTLS_10; ASSERT_TRUE(CreatePeerConnectionWrappersWithOptions(dtls_10_options, @@ -2242,7 +2538,7 @@ TEST_F(PeerConnectionIntegrationTest, Dtls10CipherStatsAndUmaMetrics) { } // Test getting cipher stats and UMA metrics when DTLS 1.2 is negotiated. -TEST_F(PeerConnectionIntegrationTest, Dtls12CipherStatsAndUmaMetrics) { +TEST_P(PeerConnectionIntegrationTest, Dtls12CipherStatsAndUmaMetrics) { PeerConnectionFactory::Options dtls_12_options; dtls_12_options.ssl_max_version = rtc::SSL_PROTOCOL_DTLS_12; ASSERT_TRUE(CreatePeerConnectionWrappersWithOptions(dtls_12_options, @@ -2268,7 +2564,7 @@ TEST_F(PeerConnectionIntegrationTest, Dtls12CipherStatsAndUmaMetrics) { // Test that DTLS 1.0 can be used if the caller supports DTLS 1.2 and the // callee only supports 1.0. -TEST_F(PeerConnectionIntegrationTest, CallerDtls12ToCalleeDtls10) { +TEST_P(PeerConnectionIntegrationTest, CallerDtls12ToCalleeDtls10) { PeerConnectionFactory::Options caller_options; caller_options.ssl_max_version = rtc::SSL_PROTOCOL_DTLS_12; PeerConnectionFactory::Options callee_options; @@ -2282,15 +2578,14 @@ TEST_F(PeerConnectionIntegrationTest, CallerDtls12ToCalleeDtls10) { callee()->AddAudioVideoTracks(); caller()->CreateAndSetAndSignalOffer(); ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); - ExpectNewFramesReceivedWithWait( - kDefaultExpectedAudioFrameCount, kDefaultExpectedVideoFrameCount, - kDefaultExpectedAudioFrameCount, kDefaultExpectedVideoFrameCount, - kMaxWaitForFramesMs); + MediaExpectations media_expectations; + media_expectations.ExpectBidirectionalAudioAndVideo(); + ASSERT_TRUE(ExpectNewFrames(media_expectations)); } // Test that DTLS 1.0 can be used if the caller only supports DTLS 1.0 and the // callee supports 1.2. -TEST_F(PeerConnectionIntegrationTest, CallerDtls10ToCalleeDtls12) { +TEST_P(PeerConnectionIntegrationTest, CallerDtls10ToCalleeDtls12) { PeerConnectionFactory::Options caller_options; caller_options.ssl_max_version = rtc::SSL_PROTOCOL_DTLS_10; PeerConnectionFactory::Options callee_options; @@ -2304,14 +2599,13 @@ TEST_F(PeerConnectionIntegrationTest, CallerDtls10ToCalleeDtls12) { callee()->AddAudioVideoTracks(); caller()->CreateAndSetAndSignalOffer(); ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); - ExpectNewFramesReceivedWithWait( - kDefaultExpectedAudioFrameCount, kDefaultExpectedVideoFrameCount, - kDefaultExpectedAudioFrameCount, kDefaultExpectedVideoFrameCount, - kMaxWaitForFramesMs); + MediaExpectations media_expectations; + media_expectations.ExpectBidirectionalAudioAndVideo(); + ASSERT_TRUE(ExpectNewFrames(media_expectations)); } // Test that a non-GCM cipher is used if both sides only support non-GCM. -TEST_F(PeerConnectionIntegrationTest, NonGcmCipherUsedWhenGcmNotSupported) { +TEST_P(PeerConnectionIntegrationTest, NonGcmCipherUsedWhenGcmNotSupported) { bool local_gcm_enabled = false; bool remote_gcm_enabled = false; int expected_cipher_suite = kDefaultSrtpCryptoSuite; @@ -2320,7 +2614,7 @@ TEST_F(PeerConnectionIntegrationTest, NonGcmCipherUsedWhenGcmNotSupported) { } // Test that a GCM cipher is used if both ends support it. -TEST_F(PeerConnectionIntegrationTest, GcmCipherUsedWhenGcmSupported) { +TEST_P(PeerConnectionIntegrationTest, GcmCipherUsedWhenGcmSupported) { bool local_gcm_enabled = true; bool remote_gcm_enabled = true; int expected_cipher_suite = kDefaultSrtpCryptoSuiteGcm; @@ -2329,7 +2623,7 @@ TEST_F(PeerConnectionIntegrationTest, GcmCipherUsedWhenGcmSupported) { } // Test that GCM isn't used if only the offerer supports it. -TEST_F(PeerConnectionIntegrationTest, +TEST_P(PeerConnectionIntegrationTest, NonGcmCipherUsedWhenOnlyCallerSupportsGcm) { bool local_gcm_enabled = true; bool remote_gcm_enabled = false; @@ -2339,7 +2633,7 @@ TEST_F(PeerConnectionIntegrationTest, } // Test that GCM isn't used if only the answerer supports it. -TEST_F(PeerConnectionIntegrationTest, +TEST_P(PeerConnectionIntegrationTest, NonGcmCipherUsedWhenOnlyCalleeSupportsGcm) { bool local_gcm_enabled = false; bool remote_gcm_enabled = true; @@ -2352,7 +2646,7 @@ TEST_F(PeerConnectionIntegrationTest, // enabled. Note that the above tests, such as GcmCipherUsedWhenGcmSupported, // only verify that a GCM cipher is negotiated, and not necessarily that SRTP // works with it. -TEST_F(PeerConnectionIntegrationTest, EndToEndCallWithGcmCipher) { +TEST_P(PeerConnectionIntegrationTest, EndToEndCallWithGcmCipher) { PeerConnectionFactory::Options gcm_options; gcm_options.crypto_options.enable_gcm_crypto_suites = true; ASSERT_TRUE( @@ -2364,15 +2658,14 @@ TEST_F(PeerConnectionIntegrationTest, EndToEndCallWithGcmCipher) { callee()->AddAudioVideoTracks(); caller()->CreateAndSetAndSignalOffer(); ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); - ExpectNewFramesReceivedWithWait( - kDefaultExpectedAudioFrameCount, kDefaultExpectedVideoFrameCount, - kDefaultExpectedAudioFrameCount, kDefaultExpectedVideoFrameCount, - kMaxWaitForFramesMs); + MediaExpectations media_expectations; + media_expectations.ExpectBidirectionalAudioAndVideo(); + ASSERT_TRUE(ExpectNewFrames(media_expectations)); } // This test sets up a call between two parties with audio, video and an RTP // data channel. -TEST_F(PeerConnectionIntegrationTest, EndToEndCallWithRtpDataChannel) { +TEST_P(PeerConnectionIntegrationTest, EndToEndCallWithRtpDataChannel) { FakeConstraints setup_constraints; setup_constraints.SetAllowRtpDataChannels(); ASSERT_TRUE(CreatePeerConnectionWrappersWithConstraints(&setup_constraints, @@ -2386,10 +2679,9 @@ TEST_F(PeerConnectionIntegrationTest, EndToEndCallWithRtpDataChannel) { caller()->CreateAndSetAndSignalOffer(); ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); // Ensure the existence of the RTP data channel didn't impede audio/video. - ExpectNewFramesReceivedWithWait( - kDefaultExpectedAudioFrameCount, kDefaultExpectedVideoFrameCount, - kDefaultExpectedAudioFrameCount, kDefaultExpectedVideoFrameCount, - kMaxWaitForFramesMs); + MediaExpectations media_expectations; + media_expectations.ExpectBidirectionalAudioAndVideo(); + ASSERT_TRUE(ExpectNewFrames(media_expectations)); ASSERT_NE(nullptr, caller()->data_channel()); ASSERT_NE(nullptr, callee()->data_channel()); EXPECT_TRUE_WAIT(caller()->data_observer()->IsOpen(), kDefaultTimeout); @@ -2407,7 +2699,7 @@ TEST_F(PeerConnectionIntegrationTest, EndToEndCallWithRtpDataChannel) { // Ensure that an RTP data channel is signaled as closed for the caller when // the callee rejects it in a subsequent offer. -TEST_F(PeerConnectionIntegrationTest, +TEST_P(PeerConnectionIntegrationTest, RtpDataChannelSignaledClosedInCalleeOffer) { // Same procedure as above test. FakeConstraints setup_constraints; @@ -2440,7 +2732,7 @@ TEST_F(PeerConnectionIntegrationTest, // transport has detected that a channel is writable and thus data can be // received before the data channel state changes to open. That is hard to test // but the same buffering is expected to be used in that case. -TEST_F(PeerConnectionIntegrationTest, +TEST_P(PeerConnectionIntegrationTest, DataBufferedUntilRtpDataChannelObserverRegistered) { // Use fake clock and simulated network delay so that we predictably can wait // until an SCTP message has been delivered without "sleep()"ing. @@ -2485,7 +2777,7 @@ TEST_F(PeerConnectionIntegrationTest, // This test sets up a call between two parties with audio, video and but only // the caller client supports RTP data channels. -TEST_F(PeerConnectionIntegrationTest, RtpDataChannelsRejectedByCallee) { +TEST_P(PeerConnectionIntegrationTest, RtpDataChannelsRejectedByCallee) { FakeConstraints setup_constraints_1; setup_constraints_1.SetAllowRtpDataChannels(); // Must disable DTLS to make negotiation succeed. @@ -2511,7 +2803,7 @@ TEST_F(PeerConnectionIntegrationTest, RtpDataChannelsRejectedByCallee) { // This test sets up a call between two parties with audio, and video. When // audio and video is setup and flowing, an RTP data channel is negotiated. -TEST_F(PeerConnectionIntegrationTest, AddRtpDataChannelInSubsequentOffer) { +TEST_P(PeerConnectionIntegrationTest, AddRtpDataChannelInSubsequentOffer) { FakeConstraints setup_constraints; setup_constraints.SetAllowRtpDataChannels(); ASSERT_TRUE(CreatePeerConnectionWrappersWithConstraints(&setup_constraints, @@ -2544,7 +2836,7 @@ TEST_F(PeerConnectionIntegrationTest, AddRtpDataChannelInSubsequentOffer) { // This test sets up a call between two parties with audio, video and an SCTP // data channel. -TEST_F(PeerConnectionIntegrationTest, EndToEndCallWithSctpDataChannel) { +TEST_P(PeerConnectionIntegrationTest, EndToEndCallWithSctpDataChannel) { ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); // Expect that data channel created on caller side will show up for callee as @@ -2555,10 +2847,9 @@ TEST_F(PeerConnectionIntegrationTest, EndToEndCallWithSctpDataChannel) { caller()->CreateAndSetAndSignalOffer(); ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); // Ensure the existence of the SCTP data channel didn't impede audio/video. - ExpectNewFramesReceivedWithWait( - kDefaultExpectedAudioFrameCount, kDefaultExpectedVideoFrameCount, - kDefaultExpectedAudioFrameCount, kDefaultExpectedVideoFrameCount, - kMaxWaitForFramesMs); + MediaExpectations media_expectations; + media_expectations.ExpectBidirectionalAudioAndVideo(); + ASSERT_TRUE(ExpectNewFrames(media_expectations)); // Caller data channel should already exist (it created one). Callee data // channel may not exist yet, since negotiation happens in-band, not in SDP. ASSERT_NE(nullptr, caller()->data_channel()); @@ -2578,7 +2869,7 @@ TEST_F(PeerConnectionIntegrationTest, EndToEndCallWithSctpDataChannel) { // Ensure that when the callee closes an SCTP data channel, the closing // procedure results in the data channel being closed for the caller as well. -TEST_F(PeerConnectionIntegrationTest, CalleeClosesSctpDataChannel) { +TEST_P(PeerConnectionIntegrationTest, CalleeClosesSctpDataChannel) { // Same procedure as above test. ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); @@ -2599,7 +2890,7 @@ TEST_F(PeerConnectionIntegrationTest, CalleeClosesSctpDataChannel) { EXPECT_TRUE_WAIT(!callee()->data_observer()->IsOpen(), kDefaultTimeout); } -TEST_F(PeerConnectionIntegrationTest, SctpDataChannelConfigSentToOtherSide) { +TEST_P(PeerConnectionIntegrationTest, SctpDataChannelConfigSentToOtherSide) { ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); webrtc::DataChannelInit init; @@ -2621,7 +2912,7 @@ TEST_F(PeerConnectionIntegrationTest, SctpDataChannelConfigSentToOtherSide) { // Test usrsctp's ability to process unordered data stream, where data actually // arrives out of order using simulated delays. Previously there have been some // bugs in this area. -TEST_F(PeerConnectionIntegrationTest, StressTestUnorderedSctpDataChannel) { +TEST_P(PeerConnectionIntegrationTest, StressTestUnorderedSctpDataChannel) { // Introduce random network delays. // Otherwise it's not a true "unordered" test. virtual_socket_server()->set_delay_mean(20); @@ -2677,7 +2968,7 @@ TEST_F(PeerConnectionIntegrationTest, StressTestUnorderedSctpDataChannel) { // This test sets up a call between two parties with audio, and video. When // audio and video are setup and flowing, an SCTP data channel is negotiated. -TEST_F(PeerConnectionIntegrationTest, AddSctpDataChannelInSubsequentOffer) { +TEST_P(PeerConnectionIntegrationTest, AddSctpDataChannelInSubsequentOffer) { ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); // Do initial offer/answer with audio/video. @@ -2709,7 +3000,7 @@ TEST_F(PeerConnectionIntegrationTest, AddSctpDataChannelInSubsequentOffer) { // to audio/video, ensuring frames are received end-to-end. Effectively the // inverse of the test above. // This was broken in M57; see https://crbug.com/711243 -TEST_F(PeerConnectionIntegrationTest, SctpDataChannelToAudioVideoUpgrade) { +TEST_P(PeerConnectionIntegrationTest, SctpDataChannelToAudioVideoUpgrade) { ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); // Do initial offer/answer with just data channel. @@ -2727,10 +3018,9 @@ TEST_F(PeerConnectionIntegrationTest, SctpDataChannelToAudioVideoUpgrade) { callee()->AddAudioVideoTracks(); caller()->CreateAndSetAndSignalOffer(); ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); - ExpectNewFramesReceivedWithWait( - kDefaultExpectedAudioFrameCount, kDefaultExpectedVideoFrameCount, - kDefaultExpectedAudioFrameCount, kDefaultExpectedVideoFrameCount, - kMaxWaitForFramesMs); + MediaExpectations media_expectations; + media_expectations.ExpectBidirectionalAudioAndVideo(); + ASSERT_TRUE(ExpectNewFrames(media_expectations)); } static void MakeSpecCompliantSctpOffer(cricket::SessionDescription* desc) { @@ -2744,7 +3034,7 @@ static void MakeSpecCompliantSctpOffer(cricket::SessionDescription* desc) { // Test that the data channel works when a spec-compliant SCTP m= section is // offered (using "a=sctp-port" instead of "a=sctpmap", and using // "UDP/DTLS/SCTP" as the protocol). -TEST_F(PeerConnectionIntegrationTest, +TEST_P(PeerConnectionIntegrationTest, DataChannelWorksWhenSpecCompliantSctpOfferReceived) { ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); @@ -2770,7 +3060,7 @@ TEST_F(PeerConnectionIntegrationTest, // Test that the ICE connection and gathering states eventually reach // "complete". -TEST_F(PeerConnectionIntegrationTest, IceStatesReachCompletion) { +TEST_P(PeerConnectionIntegrationTest, IceStatesReachCompletion) { ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); // Do normal offer/answer. @@ -2796,11 +3086,13 @@ TEST_F(PeerConnectionIntegrationTest, IceStatesReachCompletion) { // Test that firewalling the ICE connection causes the clients to identify the // disconnected state and then removing the firewall causes them to reconnect. class PeerConnectionIntegrationIceStatesTest - : public PeerConnectionIntegrationTest, - public ::testing::WithParamInterface> { + : public PeerConnectionIntegrationBaseTest, + public ::testing::WithParamInterface< + std::tuple>> { protected: - PeerConnectionIntegrationIceStatesTest() { - port_allocator_flags_ = std::get<1>(GetParam()); + PeerConnectionIntegrationIceStatesTest() + : PeerConnectionIntegrationBaseTest(std::get<0>(GetParam())) { + port_allocator_flags_ = std::get<1>(std::get<1>(GetParam())); } void StartStunServer(const SocketAddress& server_address) { @@ -2981,18 +3273,19 @@ constexpr uint32_t kFlagsIPv6NoStun = constexpr uint32_t kFlagsIPv4Stun = cricket::PORTALLOCATOR_DISABLE_TCP | cricket::PORTALLOCATOR_DISABLE_RELAY; -INSTANTIATE_TEST_CASE_P(PeerConnectionIntegrationTest, - PeerConnectionIntegrationIceStatesTest, - Values(std::make_pair("IPv4 no STUN", kFlagsIPv4NoStun), - std::make_pair("IPv6 no STUN", kFlagsIPv6NoStun), - std::make_pair("IPv4 with STUN", - kFlagsIPv4Stun))); +INSTANTIATE_TEST_CASE_P( + PeerConnectionIntegrationTest, + PeerConnectionIntegrationIceStatesTest, + Combine(Values(SdpSemantics::kPlanB, SdpSemantics::kUnifiedPlan), + Values(std::make_pair("IPv4 no STUN", kFlagsIPv4NoStun), + std::make_pair("IPv6 no STUN", kFlagsIPv6NoStun), + std::make_pair("IPv4 with STUN", kFlagsIPv4Stun)))); // This test sets up a call between two parties with audio and video. // During the call, the caller restarts ICE and the test verifies that // new ICE candidates are generated and audio and video still can flow, and the // ICE state reaches completed again. -TEST_F(PeerConnectionIntegrationTest, MediaContinuesFlowingAfterIceRestart) { +TEST_P(PeerConnectionIntegrationTest, MediaContinuesFlowingAfterIceRestart) { ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); // Do normal offer/answer and wait for ICE to complete. @@ -3061,15 +3354,14 @@ TEST_F(PeerConnectionIntegrationTest, MediaContinuesFlowingAfterIceRestart) { ASSERT_NE(callee_ufrag_pre_restart, callee_ufrag_post_restart); // Ensure that additional frames are received after the ICE restart. - ExpectNewFramesReceivedWithWait( - kDefaultExpectedAudioFrameCount, kDefaultExpectedVideoFrameCount, - kDefaultExpectedAudioFrameCount, kDefaultExpectedVideoFrameCount, - kMaxWaitForFramesMs); + MediaExpectations media_expectations; + media_expectations.ExpectBidirectionalAudioAndVideo(); + ASSERT_TRUE(ExpectNewFrames(media_expectations)); } // Verify that audio/video can be received end-to-end when ICE renomination is // enabled. -TEST_F(PeerConnectionIntegrationTest, EndToEndCallWithIceRenomination) { +TEST_P(PeerConnectionIntegrationTest, EndToEndCallWithIceRenomination) { PeerConnectionInterface::RTCConfiguration config; config.enable_ice_renomination = true; ASSERT_TRUE(CreatePeerConnectionWrappersWithConfig(config, config)); @@ -3096,16 +3388,15 @@ TEST_F(PeerConnectionIntegrationTest, EndToEndCallWithIceRenomination) { std::find(info.description.transport_options.begin(), info.description.transport_options.end(), "renomination")); } - ExpectNewFramesReceivedWithWait( - kDefaultExpectedAudioFrameCount, kDefaultExpectedVideoFrameCount, - kDefaultExpectedAudioFrameCount, kDefaultExpectedVideoFrameCount, - kMaxWaitForFramesMs); + MediaExpectations media_expectations; + media_expectations.ExpectBidirectionalAudioAndVideo(); + ASSERT_TRUE(ExpectNewFrames(media_expectations)); } // With a max bundle policy and RTCP muxing, adding a new media description to // the connection should not affect ICE at all because the new media will use // the existing connection. -TEST_F(PeerConnectionIntegrationTest, +TEST_P(PeerConnectionIntegrationTest, AddMediaToConnectedBundleDoesNotRestartIce) { PeerConnectionInterface::RTCConfiguration config; config.bundle_policy = PeerConnectionInterface::kBundlePolicyMaxBundle; @@ -3132,7 +3423,7 @@ TEST_F(PeerConnectionIntegrationTest, // This test sets up a call between two parties with audio and video. It then // renegotiates setting the video m-line to "port 0", then later renegotiates // again, enabling video. -TEST_F(PeerConnectionIntegrationTest, +TEST_P(PeerConnectionIntegrationTest, VideoFlowsAfterMediaSectionIsRejectedAndRecycled) { ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); @@ -3145,9 +3436,15 @@ TEST_F(PeerConnectionIntegrationTest, // Negotiate again, disabling the video "m=" section (the callee will set the // port to 0 due to offer_to_receive_video = 0). - PeerConnectionInterface::RTCOfferAnswerOptions options; - options.offer_to_receive_video = 0; - callee()->SetOfferAnswerOptions(options); + if (sdp_semantics_ == SdpSemantics::kPlanB) { + PeerConnectionInterface::RTCOfferAnswerOptions options; + options.offer_to_receive_video = 0; + callee()->SetOfferAnswerOptions(options); + } else { + callee()->SetRemoteOfferHandler([this] { + callee()->GetFirstTransceiverOfType(cricket::MEDIA_TYPE_VIDEO)->Stop(); + }); + } caller()->CreateAndSetAndSignalOffer(); ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); // Sanity check that video "m=" section was actually rejected. @@ -3158,24 +3455,35 @@ TEST_F(PeerConnectionIntegrationTest, // Enable video and do negotiation again, making sure video is received // end-to-end, also adding media stream to callee. - options.offer_to_receive_video = 1; - callee()->SetOfferAnswerOptions(options); - callee()->AddAudioVideoTracks(); + if (sdp_semantics_ == SdpSemantics::kPlanB) { + PeerConnectionInterface::RTCOfferAnswerOptions options; + options.offer_to_receive_video = 1; + callee()->SetOfferAnswerOptions(options); + } else { + // The caller's transceiver is stopped, so we need to add another track. + auto caller_transceiver = + caller()->GetFirstTransceiverOfType(cricket::MEDIA_TYPE_VIDEO); + EXPECT_TRUE(caller_transceiver->stopped()); + caller()->AddVideoTrack(); + } + callee()->AddVideoTrack(); + callee()->SetRemoteOfferHandler(nullptr); caller()->CreateAndSetAndSignalOffer(); ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); + // Verify the caller receives frames from the newly added stream, and the // callee receives additional frames from the re-enabled video m= section. - ExpectNewFramesReceivedWithWait( - kDefaultExpectedAudioFrameCount, kDefaultExpectedVideoFrameCount, - kDefaultExpectedAudioFrameCount, kDefaultExpectedVideoFrameCount, - kMaxWaitForFramesMs); + MediaExpectations media_expectations; + media_expectations.CalleeExpectsSomeAudio(); + media_expectations.ExpectBidirectionalVideo(); + ASSERT_TRUE(ExpectNewFrames(media_expectations)); } // This test sets up a Jsep call between two parties with external // VideoDecoderFactory. // TODO(holmer): Disabled due to sometimes crashing on buildbots. // See issue webrtc/2378. -TEST_F(PeerConnectionIntegrationTest, +TEST_P(PeerConnectionIntegrationTest, DISABLED_EndToEndCallWithVideoDecoderFactory) { ASSERT_TRUE(CreatePeerConnectionWrappers()); EnableVideoDecoderFactory(); @@ -3184,18 +3492,15 @@ TEST_F(PeerConnectionIntegrationTest, callee()->AddAudioVideoTracks(); caller()->CreateAndSetAndSignalOffer(); ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); - ExpectNewFramesReceivedWithWait( - kDefaultExpectedAudioFrameCount, kDefaultExpectedVideoFrameCount, - kDefaultExpectedAudioFrameCount, kDefaultExpectedVideoFrameCount, - kMaxWaitForFramesMs); + MediaExpectations media_expectations; + media_expectations.ExpectBidirectionalAudioAndVideo(); + ASSERT_TRUE(ExpectNewFrames(media_expectations)); } // This tests that if we negotiate after calling CreateSender but before we // have a track, then set a track later, frames from the newly-set track are // received end-to-end. -// TODO(deadbeef): Change this test to use AddTransceiver, once that's -// implemented. -TEST_F(PeerConnectionIntegrationTest, +TEST_F(PeerConnectionIntegrationTestPlanB, MediaFlowsAfterEarlyWarmupWithCreateSender) { ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); @@ -3219,17 +3524,55 @@ TEST_F(PeerConnectionIntegrationTest, EXPECT_TRUE(caller_video_sender->SetTrack(caller()->CreateLocalVideoTrack())); EXPECT_TRUE(callee_audio_sender->SetTrack(callee()->CreateLocalAudioTrack())); EXPECT_TRUE(callee_video_sender->SetTrack(callee()->CreateLocalVideoTrack())); - ExpectNewFramesReceivedWithWait( - kDefaultExpectedAudioFrameCount, kDefaultExpectedVideoFrameCount, - kDefaultExpectedAudioFrameCount, kDefaultExpectedVideoFrameCount, - kMaxWaitForFramesMs); + MediaExpectations media_expectations; + media_expectations.ExpectBidirectionalAudioAndVideo(); + ASSERT_TRUE(ExpectNewFrames(media_expectations)); +} + +// This tests that if we negotiate after calling AddTransceiver but before we +// have a track, then set a track later, frames from the newly-set tracks are +// received end-to-end. +TEST_F(PeerConnectionIntegrationTestUnifiedPlan, + MediaFlowsAfterEarlyWarmupWithAddTransceiver) { + ASSERT_TRUE(CreatePeerConnectionWrappers()); + ConnectFakeSignaling(); + auto audio_result = caller()->pc()->AddTransceiver(cricket::MEDIA_TYPE_AUDIO); + ASSERT_EQ(RTCErrorType::NONE, audio_result.error().type()); + auto caller_audio_sender = audio_result.MoveValue()->sender(); + auto video_result = caller()->pc()->AddTransceiver(cricket::MEDIA_TYPE_VIDEO); + ASSERT_EQ(RTCErrorType::NONE, video_result.error().type()); + auto caller_video_sender = video_result.MoveValue()->sender(); + callee()->SetRemoteOfferHandler([this] { + ASSERT_EQ(2u, callee()->pc()->GetTransceivers().size()); + callee()->pc()->GetTransceivers()[0]->SetDirection( + RtpTransceiverDirection::kSendRecv); + callee()->pc()->GetTransceivers()[1]->SetDirection( + RtpTransceiverDirection::kSendRecv); + }); + caller()->CreateAndSetAndSignalOffer(); + ASSERT_TRUE_WAIT(SignalingStateStable(), kMaxWaitForActivationMs); + // Wait for ICE to complete, without any tracks being set. + EXPECT_EQ_WAIT(webrtc::PeerConnectionInterface::kIceConnectionCompleted, + caller()->ice_connection_state(), kMaxWaitForFramesMs); + EXPECT_EQ_WAIT(webrtc::PeerConnectionInterface::kIceConnectionConnected, + callee()->ice_connection_state(), kMaxWaitForFramesMs); + // Now set the tracks, and expect frames to immediately start flowing. + auto callee_audio_sender = callee()->pc()->GetSenders()[0]; + auto callee_video_sender = callee()->pc()->GetSenders()[1]; + ASSERT_TRUE(caller_audio_sender->SetTrack(caller()->CreateLocalAudioTrack())); + ASSERT_TRUE(caller_video_sender->SetTrack(caller()->CreateLocalVideoTrack())); + ASSERT_TRUE(callee_audio_sender->SetTrack(callee()->CreateLocalAudioTrack())); + ASSERT_TRUE(callee_video_sender->SetTrack(callee()->CreateLocalVideoTrack())); + MediaExpectations media_expectations; + media_expectations.ExpectBidirectionalAudioAndVideo(); + ASSERT_TRUE(ExpectNewFrames(media_expectations)); } // This test verifies that a remote video track can be added via AddStream, // and sent end-to-end. For this particular test, it's simply echoed back // from the caller to the callee, rather than being forwarded to a third // PeerConnection. -TEST_F(PeerConnectionIntegrationTest, CanSendRemoteVideoTrack) { +TEST_F(PeerConnectionIntegrationTestPlanB, CanSendRemoteVideoTrack) { ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); // Just send a video track from the caller. @@ -3244,9 +3587,9 @@ TEST_F(PeerConnectionIntegrationTest, CanSendRemoteVideoTrack) { callee()->CreateAndSetAndSignalOffer(); ASSERT_TRUE_WAIT(SignalingStateStable(), kMaxWaitForActivationMs); - int expected_caller_received_video_frames = kDefaultExpectedVideoFrameCount; - ExpectNewFramesReceivedWithWait(0, expected_caller_received_video_frames, 0, - 0, kMaxWaitForFramesMs); + MediaExpectations media_expectations; + media_expectations.ExpectBidirectionalVideo(); + ASSERT_TRUE(ExpectNewFrames(media_expectations)); } // Test that we achieve the expected end-to-end connection time, using a @@ -3262,7 +3605,7 @@ TEST_F(PeerConnectionIntegrationTest, CanSendRemoteVideoTrack) { // 2. 9 media hops: Rest of the DTLS handshake. 3 hops in each direction when // using TURN<->TURN pair, and DTLS exchange is 4 packets, // the first of which should have arrived before the answer. -TEST_F(PeerConnectionIntegrationTest, EndToEndConnectionTimeWithTurnTurnPair) { +TEST_P(PeerConnectionIntegrationTest, EndToEndConnectionTimeWithTurnTurnPair) { rtc::ScopedFakeClock fake_clock; // Some things use a time of "0" as a special value, so we need to start out // the fake clock at a nonzero time. @@ -3345,8 +3688,7 @@ TEST_F(PeerConnectionIntegrationTest, EndToEndConnectionTimeWithTurnTurnPair) { // Verify that a TurnCustomizer passed in through RTCConfiguration // is actually used by the underlying TURN candidate pair. // Note that turnport_unittest.cc contains more detailed, lower-level tests. -TEST_F(PeerConnectionIntegrationTest, \ - TurnCustomizerUsedForTurnConnections) { +TEST_P(PeerConnectionIntegrationTest, TurnCustomizerUsedForTurnConnections) { static const rtc::SocketAddress turn_server_1_internal_address{"88.88.88.0", 3478}; static const rtc::SocketAddress turn_server_1_external_address{"88.88.88.1", @@ -3415,7 +3757,7 @@ TEST_F(PeerConnectionIntegrationTest, \ // In the past, this has regressed and caused crashes/black video, due to the // fact that code at some layers was doing case-insensitive comparisons and // code at other layers was not. -TEST_F(PeerConnectionIntegrationTest, CodecNamesAreCaseInsensitive) { +TEST_P(PeerConnectionIntegrationTest, CodecNamesAreCaseInsensitive) { ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); caller()->AddAudioVideoTracks(); @@ -3455,20 +3797,21 @@ TEST_F(PeerConnectionIntegrationTest, CodecNamesAreCaseInsensitive) { ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); // Verify frames are still received end-to-end. - ExpectNewFramesReceivedWithWait( - kDefaultExpectedAudioFrameCount, kDefaultExpectedVideoFrameCount, - kDefaultExpectedAudioFrameCount, kDefaultExpectedVideoFrameCount, - kMaxWaitForFramesMs); + MediaExpectations media_expectations; + media_expectations.ExpectBidirectionalAudioAndVideo(); + ASSERT_TRUE(ExpectNewFrames(media_expectations)); } -TEST_F(PeerConnectionIntegrationTest, GetSources) { +TEST_P(PeerConnectionIntegrationTest, GetSources) { ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); caller()->AddAudioTrack(); caller()->CreateAndSetAndSignalOffer(); ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); // Wait for one audio frame to be received by the callee. - ExpectNewFramesReceivedWithWait(0, 0, 1, 0, kMaxWaitForFramesMs); + MediaExpectations media_expectations; + media_expectations.CalleeExpectsSomeAudio(1); + ASSERT_TRUE(ExpectNewFrames(media_expectations)); ASSERT_GT(callee()->pc()->GetReceivers().size(), 0u); auto receiver = callee()->pc()->GetReceivers()[0]; ASSERT_EQ(receiver->media_type(), cricket::MEDIA_TYPE_AUDIO); @@ -3482,7 +3825,11 @@ TEST_F(PeerConnectionIntegrationTest, GetSources) { // Test that if a track is removed and added again with a different stream ID, // the new stream ID is successfully communicated in SDP and media continues to // flow end-to-end. -TEST_F(PeerConnectionIntegrationTest, RemoveAndAddTrackWithNewStreamId) { +// TODO(webrtc.bugs.org/8734): This test does not work for Unified Plan because +// it will not reuse a transceiver that has already been sending. After creating +// a new transceiver it tries to create an offer with two senders of the same +// track ids and it fails. +TEST_F(PeerConnectionIntegrationTestPlanB, RemoveAndAddTrackWithNewStreamId) { ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); @@ -3498,20 +3845,25 @@ TEST_F(PeerConnectionIntegrationTest, RemoveAndAddTrackWithNewStreamId) { caller()->pc()->AddTrack(track, {stream_1.get()}); caller()->CreateAndSetAndSignalOffer(); ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); - // Wait for one audio frame to be received by the callee. - ExpectNewFramesReceivedWithWait(0, 0, 1, 0, kMaxWaitForFramesMs); - + { + MediaExpectations media_expectations; + media_expectations.CalleeExpectsSomeAudio(1); + ASSERT_TRUE(ExpectNewFrames(media_expectations)); + } // Remove the sender, and create a new one with the new stream. caller()->pc()->RemoveTrack(sender); sender = caller()->pc()->AddTrack(track, {stream_2.get()}); caller()->CreateAndSetAndSignalOffer(); ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); // Wait for additional audio frames to be received by the callee. - ExpectNewFramesReceivedWithWait(0, 0, kDefaultExpectedAudioFrameCount, 0, - kMaxWaitForFramesMs); + { + MediaExpectations media_expectations; + media_expectations.CalleeExpectsSomeAudio(); + ASSERT_TRUE(ExpectNewFrames(media_expectations)); + } } -TEST_F(PeerConnectionIntegrationTest, RtcEventLogOutputWriteCalled) { +TEST_P(PeerConnectionIntegrationTest, RtcEventLogOutputWriteCalled) { ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); @@ -3530,7 +3882,7 @@ TEST_F(PeerConnectionIntegrationTest, RtcEventLogOutputWriteCalled) { // Test that if candidates are only signaled by applying full session // descriptions (instead of using AddIceCandidate), the peers can connect to // each other and exchange media. -TEST_F(PeerConnectionIntegrationTest, MediaFlowsWhenCandidatesSetOnlyInSdp) { +TEST_P(PeerConnectionIntegrationTest, MediaFlowsWhenCandidatesSetOnlyInSdp) { ASSERT_TRUE(CreatePeerConnectionWrappers()); // Each side will signal the session descriptions but not candidates. ConnectFakeSignalingForSdpOnly(); @@ -3553,17 +3905,16 @@ TEST_F(PeerConnectionIntegrationTest, MediaFlowsWhenCandidatesSetOnlyInSdp) { ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); // Ensure that media flows in both directions. - ExpectNewFramesReceivedWithWait( - kDefaultExpectedAudioFrameCount, kDefaultExpectedVideoFrameCount, - kDefaultExpectedAudioFrameCount, kDefaultExpectedVideoFrameCount, - kMaxWaitForFramesMs); + MediaExpectations media_expectations; + media_expectations.ExpectBidirectionalAudioAndVideo(); + ASSERT_TRUE(ExpectNewFrames(media_expectations)); } // Test that SetAudioPlayout can be used to disable audio playout from the // start, then later enable it. This may be useful, for example, if the caller // needs to play a local ringtone until some event occurs, after which it // switches to playing the received audio. -TEST_F(PeerConnectionIntegrationTest, DisableAndEnableAudioPlayout) { +TEST_P(PeerConnectionIntegrationTest, DisableAndEnableAudioPlayout) { ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); @@ -3585,9 +3936,9 @@ TEST_F(PeerConnectionIntegrationTest, DisableAndEnableAudioPlayout) { // Enable playout again, and ensure audio starts flowing. caller()->pc()->SetAudioPlayout(true); - ExpectNewFramesReceivedWithWait(kDefaultExpectedAudioFrameCount, 0, - kDefaultExpectedAudioFrameCount, 0, - kMaxWaitForFramesMs); + MediaExpectations media_expectations; + media_expectations.ExpectBidirectionalAudio(); + ASSERT_TRUE(ExpectNewFrames(media_expectations)); } double GetAudioEnergyStat(PeerConnectionWrapper* pc) { @@ -3611,7 +3962,7 @@ double GetAudioEnergyStat(PeerConnectionWrapper* pc) { // Test that if audio playout is disabled via the SetAudioPlayout() method, then // incoming audio is still processed and statistics are generated. -TEST_F(PeerConnectionIntegrationTest, +TEST_P(PeerConnectionIntegrationTest, DisableAudioPlayoutStillGeneratesAudioStats) { ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); @@ -3633,7 +3984,7 @@ TEST_F(PeerConnectionIntegrationTest, // start, then later enable it. This may be useful, for example, if the caller // wants to ensure that no audio resources are active before a certain state // is reached. -TEST_F(PeerConnectionIntegrationTest, DisableAndEnableAudioRecording) { +TEST_P(PeerConnectionIntegrationTest, DisableAndEnableAudioRecording) { ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); @@ -3655,14 +4006,14 @@ TEST_F(PeerConnectionIntegrationTest, DisableAndEnableAudioRecording) { // Enable audio recording again, and ensure audio starts flowing. caller()->pc()->SetAudioRecording(true); - ExpectNewFramesReceivedWithWait(kDefaultExpectedAudioFrameCount, 0, - kDefaultExpectedAudioFrameCount, 0, - kMaxWaitForFramesMs); + MediaExpectations media_expectations; + media_expectations.ExpectBidirectionalAudio(); + ASSERT_TRUE(ExpectNewFrames(media_expectations)); } // Test that after closing PeerConnections, they stop sending any packets (ICE, // DTLS, RTP...). -TEST_F(PeerConnectionIntegrationTest, ClosingConnectionStopsPacketFlow) { +TEST_P(PeerConnectionIntegrationTest, ClosingConnectionStopsPacketFlow) { // Set up audio/video/data, wait for some frames to be received. ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); @@ -3672,9 +4023,9 @@ TEST_F(PeerConnectionIntegrationTest, ClosingConnectionStopsPacketFlow) { #endif caller()->CreateAndSetAndSignalOffer(); ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); - ExpectNewFramesReceivedWithWait(0, 0, kDefaultExpectedAudioFrameCount, - kDefaultExpectedAudioFrameCount, - kMaxWaitForFramesMs); + MediaExpectations media_expectations; + media_expectations.CalleeExpectsSomeAudioAndVideo(); + ASSERT_TRUE(ExpectNewFrames(media_expectations)); // Close PeerConnections. caller()->pc()->Close(); callee()->pc()->Close(); @@ -3685,32 +4036,24 @@ TEST_F(PeerConnectionIntegrationTest, ClosingConnectionStopsPacketFlow) { EXPECT_EQ(sent_packets_a, sent_packets_b); } -// Test that a basic 1 audio and 1 video track call works when Unified Plan -// semantics configured for both sides. -TEST_F(PeerConnectionIntegrationTest, UnifiedPlanMediaFlows) { - PeerConnectionInterface::RTCConfiguration config; - config.sdp_semantics = SdpSemantics::kUnifiedPlan; - ASSERT_TRUE(CreatePeerConnectionWrappersWithConfig(config, config)); - ConnectFakeSignaling(); - caller()->AddAudioVideoTracks(); - callee()->AddAudioVideoTracks(); - caller()->CreateAndSetAndSignalOffer(); - ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); - ExpectNewFramesReceivedWithWait( - kDefaultExpectedAudioFrameCount, kDefaultExpectedVideoFrameCount, - kDefaultExpectedAudioFrameCount, kDefaultExpectedVideoFrameCount, - kMaxWaitForFramesMs); -} +INSTANTIATE_TEST_CASE_P(PeerConnectionIntegrationTest, + PeerConnectionIntegrationTest, + Values(SdpSemantics::kPlanB, + SdpSemantics::kUnifiedPlan)); // Tests that verify interoperability between Plan B and Unified Plan // PeerConnections. class PeerConnectionIntegrationInteropTest - : public PeerConnectionIntegrationTest, + : public PeerConnectionIntegrationBaseTest, public ::testing::WithParamInterface< std::tuple> { protected: + // Setting the SdpSemantics for the base test to kDefault does not matter + // because we specify not to use the test semantics when creating + // PeerConnectionWrappers. PeerConnectionIntegrationInteropTest() - : caller_semantics_(std::get<0>(GetParam())), + : PeerConnectionIntegrationBaseTest(SdpSemantics::kDefault), + caller_semantics_(std::get<0>(GetParam())), callee_semantics_(std::get<1>(GetParam())) {} bool CreatePeerConnectionWrappersWithSemantics() { @@ -3748,9 +4091,9 @@ TEST_P(PeerConnectionIntegrationInteropTest, OneAudioLocalToNoMediaRemote) { EXPECT_EQ(cricket::MEDIA_TYPE_AUDIO, receivers[0]->media_type()); EXPECT_EQ(receivers[0]->track()->id(), audio_sender->track()->id()); - // Expect to receive only audio frames on the callee. - ExpectNewFramesReceivedWithWait(0, 0, kDefaultExpectedAudioFrameCount, 0, - kMaxWaitForFramesMs); + MediaExpectations media_expectations; + media_expectations.CalleeExpectsSomeAudio(); + ASSERT_TRUE(ExpectNewFrames(media_expectations)); } TEST_P(PeerConnectionIntegrationInteropTest, OneAudioOneVideoToNoMediaRemote) { @@ -3773,10 +4116,9 @@ TEST_P(PeerConnectionIntegrationInteropTest, OneAudioOneVideoToNoMediaRemote) { ASSERT_EQ(1u, video_receivers.size()); EXPECT_EQ(video_receivers[0]->track()->id(), video_sender->track()->id()); - // Expect to receive audio and video frames only on the callee. - ExpectNewFramesReceivedWithWait(0, 0, kDefaultExpectedAudioFrameCount, - kDefaultExpectedVideoFrameCount, - kMaxWaitForFramesMs); + MediaExpectations media_expectations; + media_expectations.CalleeExpectsSomeAudioAndVideo(); + ASSERT_TRUE(ExpectNewFrames(media_expectations)); } TEST_P(PeerConnectionIntegrationInteropTest, @@ -3789,10 +4131,9 @@ TEST_P(PeerConnectionIntegrationInteropTest, caller()->CreateAndSetAndSignalOffer(); ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); - ExpectNewFramesReceivedWithWait( - kDefaultExpectedAudioFrameCount, kDefaultExpectedVideoFrameCount, - kDefaultExpectedAudioFrameCount, kDefaultExpectedVideoFrameCount, - kMaxWaitForFramesMs); + MediaExpectations media_expectations; + media_expectations.ExpectBidirectionalAudioAndVideo(); + ASSERT_TRUE(ExpectNewFrames(media_expectations)); } TEST_P(PeerConnectionIntegrationInteropTest, @@ -3813,11 +4154,10 @@ TEST_P(PeerConnectionIntegrationInteropTest, callee()->CreateAndSetAndSignalOffer(); ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); - // Expect to receive audio frames on the callee and video frames on the - // caller. - ExpectNewFramesReceivedWithWait(0, kDefaultExpectedVideoFrameCount, - kDefaultExpectedAudioFrameCount, 0, - kMaxWaitForFramesMs); + MediaExpectations media_expectations; + media_expectations.CallerExpectsSomeVideo(); + media_expectations.CalleeExpectsSomeAudio(); + ASSERT_TRUE(ExpectNewFrames(media_expectations)); } // Test that if one side offers two video tracks then the other side will only @@ -3838,10 +4178,9 @@ TEST_P(PeerConnectionIntegrationInteropTest, TwoVideoLocalToNoMediaRemote) { EXPECT_TRUE(receivers[0]->track()->enabled()); EXPECT_EQ(first_sender->track()->id(), receivers[0]->track()->id()); - // Expect to receive video frames from the one track. - ExpectNewFramesReceivedWithWait(0, 0, - 0, kDefaultExpectedVideoFrameCount, - kMaxWaitForFramesMs); + MediaExpectations media_expectations; + media_expectations.CalleeExpectsSomeVideo(); + ASSERT_TRUE(ExpectNewFrames(media_expectations)); } // Test that in the multi-track case each endpoint only looks at the first track @@ -3893,9 +4232,9 @@ TEST_P(PeerConnectionIntegrationInteropTest, TwoVideoLocalToTwoVideoRemote) { } // Should be exchanging video frames for the first tracks on each endpoint. - ExpectNewFramesReceivedWithWait(0, kDefaultExpectedVideoFrameCount, 0, - kDefaultExpectedVideoFrameCount, - kMaxWaitForFramesMs); + MediaExpectations media_expectations; + media_expectations.ExpectBidirectionalVideo(); + ASSERT_TRUE(ExpectNewFrames(media_expectations)); } INSTANTIATE_TEST_CASE_P(