blob: 97e5ecdc40417253b535abfb1eac7be7832bee5f [file] [log] [blame]
/*
* Copyright 2012 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
// Integration tests for PeerConnection to exercise the options of
// either splitting or not splitting the MediaChannel object.
// These tests exercise a full stack over a simulated network.
// TODO(bugs.webrtc.org/13931): Delete these tests when split is landed.
#include <string>
#include <vector>
#include "absl/types/optional.h"
#include "api/media_stream_interface.h"
#include "api/peer_connection_interface.h"
#include "api/rtc_error.h"
#include "api/scoped_refptr.h"
#include "api/stats/rtc_stats.h"
#include "api/stats/rtc_stats_report.h"
#include "api/stats/rtcstats_objects.h"
#include "media/base/codec.h"
#include "media/base/media_constants.h"
#include "pc/session_description.h"
#include "pc/test/integration_test_helpers.h"
#include "rtc_base/gunit.h"
#include "rtc_base/virtual_socket_server.h"
#include "test/gtest.h"
namespace webrtc {
namespace {
class PeerConnectionMediaChannelSplitTest
: public PeerConnectionIntegrationBaseTest,
public ::testing::WithParamInterface<std::string> {
protected:
PeerConnectionMediaChannelSplitTest()
: PeerConnectionIntegrationBaseTest(SdpSemantics::kUnifiedPlan,
/* field_trials = */ GetParam()) {}
};
int NacksReceivedCount(PeerConnectionIntegrationWrapper& pc) {
rtc::scoped_refptr<const webrtc::RTCStatsReport> report = pc.NewGetStats();
auto sender_stats = report->GetStatsOfType<RTCOutboundRtpStreamStats>();
if (sender_stats.size() != 1) {
ADD_FAILURE();
return 0;
}
if (!sender_stats[0]->nack_count.is_defined()) {
return 0;
}
return *sender_stats[0]->nack_count;
}
int NacksSentCount(PeerConnectionIntegrationWrapper& pc) {
rtc::scoped_refptr<const webrtc::RTCStatsReport> report = pc.NewGetStats();
auto receiver_stats = report->GetStatsOfType<RTCInboundRtpStreamStats>();
if (receiver_stats.size() != 1) {
ADD_FAILURE();
return 0;
}
if (!receiver_stats[0]->nack_count.is_defined()) {
return 0;
}
return *receiver_stats[0]->nack_count;
}
// Test disabled because it is flaky.
TEST_P(PeerConnectionMediaChannelSplitTest,
DISABLED_AudioPacketLossCausesNack) {
RTCConfiguration config;
ASSERT_TRUE(CreatePeerConnectionWrappersWithConfig(config, config));
ConnectFakeSignaling();
auto audio_transceiver_or_error =
caller()->pc()->AddTransceiver(caller()->CreateLocalAudioTrack());
ASSERT_TRUE(audio_transceiver_or_error.ok());
auto send_transceiver = audio_transceiver_or_error.MoveValue();
// Munge the SDP to include NACK and RRTR on Opus, and remove all other
// codecs.
caller()->SetGeneratedSdpMunger([](cricket::SessionDescription* desc) {
for (ContentInfo& content : desc->contents()) {
cricket::AudioContentDescription* media =
content.media_description()->as_audio();
std::vector<cricket::AudioCodec> codecs = media->codecs();
std::vector<cricket::AudioCodec> codecs_out;
for (cricket::AudioCodec codec : codecs) {
if (codec.name == "opus") {
codec.AddFeedbackParam(cricket::FeedbackParam(
cricket::kRtcpFbParamNack, cricket::kParamValueEmpty));
codec.AddFeedbackParam(cricket::FeedbackParam(
cricket::kRtcpFbParamRrtr, cricket::kParamValueEmpty));
codecs_out.push_back(codec);
}
}
EXPECT_FALSE(codecs_out.empty());
media->set_codecs(codecs_out);
}
});
caller()->CreateAndSetAndSignalOffer();
// Check for failure in helpers
ASSERT_FALSE(HasFailure());
MediaExpectations media_expectations;
media_expectations.CalleeExpectsSomeAudio(1);
ExpectNewFrames(media_expectations);
ASSERT_FALSE(HasFailure());
virtual_socket_server()->set_drop_probability(0.2);
// Wait until callee has sent at least one NACK.
// Note that due to stats caching, this might only be visible 50 ms
// after the nack was in fact sent.
EXPECT_TRUE_WAIT(NacksSentCount(*callee()) > 0, kDefaultTimeout);
ASSERT_FALSE(HasFailure());
virtual_socket_server()->set_drop_probability(0.0);
// Wait until caller has received at least one NACK
EXPECT_TRUE_WAIT(NacksReceivedCount(*caller()) > 0, kDefaultTimeout);
}
TEST_P(PeerConnectionMediaChannelSplitTest, VideoPacketLossCausesNack) {
RTCConfiguration config;
ASSERT_TRUE(CreatePeerConnectionWrappersWithConfig(config, config));
ConnectFakeSignaling();
auto video_transceiver_or_error =
caller()->pc()->AddTransceiver(caller()->CreateLocalVideoTrack());
ASSERT_TRUE(video_transceiver_or_error.ok());
auto send_transceiver = video_transceiver_or_error.MoveValue();
// Munge the SDP to include NACK and RRTR on VP8, and remove all other
// codecs.
caller()->SetGeneratedSdpMunger([](cricket::SessionDescription* desc) {
for (ContentInfo& content : desc->contents()) {
cricket::VideoContentDescription* media =
content.media_description()->as_video();
std::vector<cricket::VideoCodec> codecs = media->codecs();
std::vector<cricket::VideoCodec> codecs_out;
for (cricket::VideoCodec codec : codecs) {
if (codec.name == "VP8") {
ASSERT_TRUE(codec.HasFeedbackParam(cricket::FeedbackParam(
cricket::kRtcpFbParamNack, cricket::kParamValueEmpty)));
codecs_out.push_back(codec);
}
}
EXPECT_FALSE(codecs_out.empty());
media->set_codecs(codecs_out);
}
});
caller()->CreateAndSetAndSignalOffer();
// Check for failure in helpers
ASSERT_FALSE(HasFailure());
MediaExpectations media_expectations;
media_expectations.CalleeExpectsSomeVideo(1);
ExpectNewFrames(media_expectations);
ASSERT_FALSE(HasFailure());
virtual_socket_server()->set_drop_probability(0.2);
// Wait until callee has sent at least one NACK.
// Note that due to stats caching, this might only be visible 50 ms
// after the nack was in fact sent.
EXPECT_TRUE_WAIT(NacksSentCount(*callee()) > 0, kDefaultTimeout);
ASSERT_FALSE(HasFailure());
// Wait until caller has received at least one NACK
EXPECT_TRUE_WAIT(NacksReceivedCount(*caller()) > 0, kDefaultTimeout);
}
// Test that we can get capture start ntp time.
TEST_P(PeerConnectionMediaChannelSplitTest,
GetCaptureStartNtpTimeWithOldStatsApi) {
ASSERT_TRUE(CreatePeerConnectionWrappers());
ConnectFakeSignaling();
caller()->AddAudioTrack();
callee()->AddAudioTrack();
// Do offer/answer, wait for the callee to receive some frames.
caller()->CreateAndSetAndSignalOffer();
ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
// Get the remote audio track created on the receiver, so they can be used as
// GetStats filters.
auto receivers = callee()->pc()->GetReceivers();
ASSERT_EQ(1u, receivers.size());
auto remote_audio_track = receivers[0]->track();
// Get the audio output level stats. Note that the level is not available
// until an RTCP packet has been received.
EXPECT_TRUE_WAIT(callee()->OldGetStatsForTrack(remote_audio_track.get())
->CaptureStartNtpTime() > 0,
2 * kMaxWaitForFramesMs);
}
INSTANTIATE_TEST_SUITE_P(PeerConnectionMediaChannelSplitTest,
PeerConnectionMediaChannelSplitTest,
Values("WebRTC-SplitMediaChannel/Disabled/",
"WebRTC-SplitMediaChannel/Enabled/"));
} // namespace
} // namespace webrtc