by storing [[LastCreatedOffer]] / [[LastCreatedAnswer]] which are similar to the W3C equivalent but as description objects instead of serialized SDP strings. While rejecting all SDP munging is not feasible, this lets us measure and reject certain modifications gradually. Chromium metrics CL: https://chromium-review.googlesource.com/c/chromium/src/+/6089633 This is measured at three points during the lifetime of a peerconnection: * for the first SLD call * when the connection is first established * when the connection was established and is being closed Note that the "first" SDP munging detected is returned which may hide that something uses more than one modification. BUG=chromium:40567530 Change-Id: I964e3ee6e75f73b777d90556fac8691a6f3dc27f Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/370680 Reviewed-by: Harald Alvestrand <hta@webrtc.org> Reviewed-by: Henrik Boström <hbos@webrtc.org> Reviewed-by: Johannes Kron <kron@webrtc.org> Commit-Queue: Henrik Boström <hbos@webrtc.org> Cr-Commit-Position: refs/heads/main@{#43741}
385 lines
12 KiB
C++
385 lines
12 KiB
C++
/*
|
|
* Copyright 2017 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.
|
|
*/
|
|
|
|
#include "pc/peer_connection_wrapper.h"
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <memory>
|
|
#include <optional>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include "api/data_channel_interface.h"
|
|
#include "api/function_view.h"
|
|
#include "api/jsep.h"
|
|
#include "api/make_ref_counted.h"
|
|
#include "api/media_stream_interface.h"
|
|
#include "api/media_types.h"
|
|
#include "api/peer_connection_interface.h"
|
|
#include "api/rtc_error.h"
|
|
#include "api/rtp_parameters.h"
|
|
#include "api/rtp_sender_interface.h"
|
|
#include "api/rtp_transceiver_interface.h"
|
|
#include "api/scoped_refptr.h"
|
|
#include "api/stats/rtc_stats_report.h"
|
|
#include "api/test/rtc_error_matchers.h"
|
|
#include "pc/sdp_utils.h"
|
|
#include "pc/test/fake_video_track_source.h"
|
|
#include "pc/test/mock_peer_connection_observers.h"
|
|
#include "rtc_base/checks.h"
|
|
#include "rtc_base/logging.h"
|
|
#include "test/gmock.h"
|
|
#include "test/gtest.h"
|
|
#include "test/wait_until.h"
|
|
|
|
namespace webrtc {
|
|
|
|
using ::testing::Eq;
|
|
using RTCOfferAnswerOptions = PeerConnectionInterface::RTCOfferAnswerOptions;
|
|
|
|
PeerConnectionWrapper::PeerConnectionWrapper(
|
|
rtc::scoped_refptr<PeerConnectionFactoryInterface> pc_factory,
|
|
rtc::scoped_refptr<PeerConnectionInterface> pc,
|
|
std::unique_ptr<MockPeerConnectionObserver> observer)
|
|
: pc_factory_(std::move(pc_factory)),
|
|
observer_(std::move(observer)),
|
|
pc_(std::move(pc)) {
|
|
RTC_DCHECK(pc_factory_);
|
|
RTC_DCHECK(pc_);
|
|
RTC_DCHECK(observer_);
|
|
observer_->SetPeerConnectionInterface(pc_.get());
|
|
}
|
|
|
|
PeerConnectionWrapper::~PeerConnectionWrapper() {
|
|
if (pc_)
|
|
pc_->Close();
|
|
}
|
|
|
|
PeerConnectionFactoryInterface* PeerConnectionWrapper::pc_factory() {
|
|
return pc_factory_.get();
|
|
}
|
|
|
|
PeerConnectionInterface* PeerConnectionWrapper::pc() {
|
|
return pc_.get();
|
|
}
|
|
|
|
MockPeerConnectionObserver* PeerConnectionWrapper::observer() {
|
|
return observer_.get();
|
|
}
|
|
|
|
std::unique_ptr<SessionDescriptionInterface>
|
|
PeerConnectionWrapper::CreateOffer() {
|
|
return CreateOffer(RTCOfferAnswerOptions());
|
|
}
|
|
|
|
std::unique_ptr<SessionDescriptionInterface> PeerConnectionWrapper::CreateOffer(
|
|
const PeerConnectionInterface::RTCOfferAnswerOptions& options,
|
|
std::string* error_out) {
|
|
return CreateSdp(
|
|
[this, options](CreateSessionDescriptionObserver* observer) {
|
|
pc()->CreateOffer(observer, options);
|
|
},
|
|
error_out);
|
|
}
|
|
|
|
std::unique_ptr<SessionDescriptionInterface>
|
|
PeerConnectionWrapper::CreateOfferAndSetAsLocal() {
|
|
return CreateOfferAndSetAsLocal(RTCOfferAnswerOptions());
|
|
}
|
|
|
|
std::unique_ptr<SessionDescriptionInterface>
|
|
PeerConnectionWrapper::CreateOfferAndSetAsLocal(
|
|
const PeerConnectionInterface::RTCOfferAnswerOptions& options) {
|
|
auto offer = CreateOffer(options);
|
|
if (!offer) {
|
|
return nullptr;
|
|
}
|
|
EXPECT_TRUE(SetLocalDescription(CloneSessionDescription(offer.get())));
|
|
return offer;
|
|
}
|
|
|
|
std::unique_ptr<SessionDescriptionInterface>
|
|
PeerConnectionWrapper::CreateAnswer() {
|
|
return CreateAnswer(RTCOfferAnswerOptions());
|
|
}
|
|
|
|
std::unique_ptr<SessionDescriptionInterface>
|
|
PeerConnectionWrapper::CreateAnswer(
|
|
const PeerConnectionInterface::RTCOfferAnswerOptions& options,
|
|
std::string* error_out) {
|
|
return CreateSdp(
|
|
[this, options](CreateSessionDescriptionObserver* observer) {
|
|
pc()->CreateAnswer(observer, options);
|
|
},
|
|
error_out);
|
|
}
|
|
|
|
std::unique_ptr<SessionDescriptionInterface>
|
|
PeerConnectionWrapper::CreateAnswerAndSetAsLocal() {
|
|
return CreateAnswerAndSetAsLocal(RTCOfferAnswerOptions());
|
|
}
|
|
|
|
std::unique_ptr<SessionDescriptionInterface>
|
|
PeerConnectionWrapper::CreateAnswerAndSetAsLocal(
|
|
const PeerConnectionInterface::RTCOfferAnswerOptions& options) {
|
|
auto answer = CreateAnswer(options);
|
|
if (!answer) {
|
|
return nullptr;
|
|
}
|
|
EXPECT_TRUE(SetLocalDescription(CloneSessionDescription(answer.get())));
|
|
return answer;
|
|
}
|
|
|
|
std::unique_ptr<SessionDescriptionInterface>
|
|
PeerConnectionWrapper::CreateRollback() {
|
|
return CreateSessionDescription(SdpType::kRollback, "");
|
|
}
|
|
|
|
std::unique_ptr<SessionDescriptionInterface> PeerConnectionWrapper::CreateSdp(
|
|
rtc::FunctionView<void(CreateSessionDescriptionObserver*)> fn,
|
|
std::string* error_out) {
|
|
auto observer = rtc::make_ref_counted<MockCreateSessionDescriptionObserver>();
|
|
fn(observer.get());
|
|
EXPECT_THAT(
|
|
WaitUntil([&] { return observer->called(); }, ::testing::IsTrue()),
|
|
IsRtcOk());
|
|
if (error_out && !observer->result()) {
|
|
*error_out = observer->error();
|
|
}
|
|
return observer->MoveDescription();
|
|
}
|
|
|
|
bool PeerConnectionWrapper::SetLocalDescription(
|
|
std::unique_ptr<SessionDescriptionInterface> desc,
|
|
std::string* error_out) {
|
|
return SetSdp(
|
|
[this, &desc](SetSessionDescriptionObserver* observer) {
|
|
pc()->SetLocalDescription(observer, desc.release());
|
|
},
|
|
error_out);
|
|
}
|
|
|
|
bool PeerConnectionWrapper::SetLocalDescription(
|
|
std::unique_ptr<SessionDescriptionInterface> desc,
|
|
RTCError* error_out) {
|
|
auto observer = rtc::make_ref_counted<FakeSetLocalDescriptionObserver>();
|
|
pc()->SetLocalDescription(std::move(desc), observer);
|
|
EXPECT_THAT(
|
|
WaitUntil([&] { return observer->called(); }, ::testing::IsTrue()),
|
|
IsRtcOk());
|
|
bool ok = observer->error().ok();
|
|
if (error_out)
|
|
*error_out = std::move(observer->error());
|
|
return ok;
|
|
}
|
|
|
|
bool PeerConnectionWrapper::SetRemoteDescription(
|
|
std::unique_ptr<SessionDescriptionInterface> desc,
|
|
std::string* error_out) {
|
|
return SetSdp(
|
|
[this, &desc](SetSessionDescriptionObserver* observer) {
|
|
pc()->SetRemoteDescription(observer, desc.release());
|
|
},
|
|
error_out);
|
|
}
|
|
|
|
bool PeerConnectionWrapper::SetRemoteDescription(
|
|
std::unique_ptr<SessionDescriptionInterface> desc,
|
|
RTCError* error_out) {
|
|
auto observer = rtc::make_ref_counted<FakeSetRemoteDescriptionObserver>();
|
|
pc()->SetRemoteDescription(std::move(desc), observer);
|
|
EXPECT_THAT(
|
|
WaitUntil([&] { return observer->called(); }, ::testing::IsTrue()),
|
|
IsRtcOk());
|
|
bool ok = observer->error().ok();
|
|
if (error_out)
|
|
*error_out = std::move(observer->error());
|
|
return ok;
|
|
}
|
|
|
|
bool PeerConnectionWrapper::SetSdp(
|
|
rtc::FunctionView<void(SetSessionDescriptionObserver*)> fn,
|
|
std::string* error_out) {
|
|
auto observer = rtc::make_ref_counted<MockSetSessionDescriptionObserver>();
|
|
fn(observer.get());
|
|
EXPECT_THAT(
|
|
WaitUntil([&] { return observer->called(); }, ::testing::IsTrue()),
|
|
IsRtcOk());
|
|
if (error_out && !observer->result()) {
|
|
*error_out = observer->error();
|
|
}
|
|
return observer->result();
|
|
}
|
|
|
|
bool PeerConnectionWrapper::ExchangeOfferAnswerWith(
|
|
PeerConnectionWrapper* answerer) {
|
|
return ExchangeOfferAnswerWith(answerer, RTCOfferAnswerOptions(),
|
|
RTCOfferAnswerOptions());
|
|
}
|
|
|
|
bool PeerConnectionWrapper::ExchangeOfferAnswerWith(
|
|
PeerConnectionWrapper* answerer,
|
|
const PeerConnectionInterface::RTCOfferAnswerOptions& offer_options,
|
|
const PeerConnectionInterface::RTCOfferAnswerOptions& answer_options) {
|
|
RTC_DCHECK(answerer);
|
|
if (answerer == this) {
|
|
RTC_LOG(LS_ERROR) << "Cannot exchange offer/answer with ourself!";
|
|
return false;
|
|
}
|
|
auto offer = CreateOffer(offer_options);
|
|
EXPECT_TRUE(offer);
|
|
if (!offer) {
|
|
return false;
|
|
}
|
|
bool set_local_offer =
|
|
SetLocalDescription(CloneSessionDescription(offer.get()));
|
|
EXPECT_TRUE(set_local_offer);
|
|
if (!set_local_offer) {
|
|
return false;
|
|
}
|
|
bool set_remote_offer = answerer->SetRemoteDescription(std::move(offer));
|
|
EXPECT_TRUE(set_remote_offer);
|
|
if (!set_remote_offer) {
|
|
return false;
|
|
}
|
|
auto answer = answerer->CreateAnswer(answer_options);
|
|
EXPECT_TRUE(answer);
|
|
if (!answer) {
|
|
return false;
|
|
}
|
|
bool set_local_answer =
|
|
answerer->SetLocalDescription(CloneSessionDescription(answer.get()));
|
|
EXPECT_TRUE(set_local_answer);
|
|
if (!set_local_answer) {
|
|
return false;
|
|
}
|
|
bool set_remote_answer = SetRemoteDescription(std::move(answer));
|
|
EXPECT_TRUE(set_remote_answer);
|
|
return set_remote_answer;
|
|
}
|
|
|
|
rtc::scoped_refptr<RtpTransceiverInterface>
|
|
PeerConnectionWrapper::AddTransceiver(cricket::MediaType media_type) {
|
|
RTCErrorOr<rtc::scoped_refptr<RtpTransceiverInterface>> result =
|
|
pc()->AddTransceiver(media_type);
|
|
EXPECT_EQ(RTCErrorType::NONE, result.error().type());
|
|
return result.MoveValue();
|
|
}
|
|
|
|
rtc::scoped_refptr<RtpTransceiverInterface>
|
|
PeerConnectionWrapper::AddTransceiver(cricket::MediaType media_type,
|
|
const RtpTransceiverInit& init) {
|
|
RTCErrorOr<rtc::scoped_refptr<RtpTransceiverInterface>> result =
|
|
pc()->AddTransceiver(media_type, init);
|
|
EXPECT_EQ(RTCErrorType::NONE, result.error().type());
|
|
return result.MoveValue();
|
|
}
|
|
|
|
rtc::scoped_refptr<RtpTransceiverInterface>
|
|
PeerConnectionWrapper::AddTransceiver(
|
|
rtc::scoped_refptr<MediaStreamTrackInterface> track) {
|
|
RTCErrorOr<rtc::scoped_refptr<RtpTransceiverInterface>> result =
|
|
pc()->AddTransceiver(track);
|
|
EXPECT_EQ(RTCErrorType::NONE, result.error().type());
|
|
return result.MoveValue();
|
|
}
|
|
|
|
rtc::scoped_refptr<RtpTransceiverInterface>
|
|
PeerConnectionWrapper::AddTransceiver(
|
|
rtc::scoped_refptr<MediaStreamTrackInterface> track,
|
|
const RtpTransceiverInit& init) {
|
|
RTCErrorOr<rtc::scoped_refptr<RtpTransceiverInterface>> result =
|
|
pc()->AddTransceiver(track, init);
|
|
EXPECT_EQ(RTCErrorType::NONE, result.error().type());
|
|
return result.MoveValue();
|
|
}
|
|
|
|
rtc::scoped_refptr<AudioTrackInterface> PeerConnectionWrapper::CreateAudioTrack(
|
|
const std::string& label) {
|
|
return pc_factory()->CreateAudioTrack(label, nullptr);
|
|
}
|
|
|
|
rtc::scoped_refptr<VideoTrackInterface> PeerConnectionWrapper::CreateVideoTrack(
|
|
const std::string& label) {
|
|
return pc_factory()->CreateVideoTrack(FakeVideoTrackSource::Create(), label);
|
|
}
|
|
|
|
rtc::scoped_refptr<RtpSenderInterface> PeerConnectionWrapper::AddTrack(
|
|
rtc::scoped_refptr<MediaStreamTrackInterface> track,
|
|
const std::vector<std::string>& stream_ids) {
|
|
RTCErrorOr<rtc::scoped_refptr<RtpSenderInterface>> result =
|
|
pc()->AddTrack(track, stream_ids);
|
|
EXPECT_EQ(RTCErrorType::NONE, result.error().type());
|
|
return result.MoveValue();
|
|
}
|
|
|
|
rtc::scoped_refptr<RtpSenderInterface> PeerConnectionWrapper::AddTrack(
|
|
rtc::scoped_refptr<MediaStreamTrackInterface> track,
|
|
const std::vector<std::string>& stream_ids,
|
|
const std::vector<RtpEncodingParameters>& init_send_encodings) {
|
|
RTCErrorOr<rtc::scoped_refptr<RtpSenderInterface>> result =
|
|
pc()->AddTrack(track, stream_ids, init_send_encodings);
|
|
EXPECT_EQ(RTCErrorType::NONE, result.error().type());
|
|
return result.MoveValue();
|
|
}
|
|
|
|
rtc::scoped_refptr<RtpSenderInterface> PeerConnectionWrapper::AddAudioTrack(
|
|
const std::string& track_label,
|
|
const std::vector<std::string>& stream_ids) {
|
|
return AddTrack(CreateAudioTrack(track_label), stream_ids);
|
|
}
|
|
|
|
rtc::scoped_refptr<RtpSenderInterface> PeerConnectionWrapper::AddVideoTrack(
|
|
const std::string& track_label,
|
|
const std::vector<std::string>& stream_ids) {
|
|
return AddTrack(CreateVideoTrack(track_label), stream_ids);
|
|
}
|
|
|
|
rtc::scoped_refptr<DataChannelInterface>
|
|
PeerConnectionWrapper::CreateDataChannel(
|
|
const std::string& label,
|
|
const std::optional<DataChannelInit>& config) {
|
|
const DataChannelInit* config_ptr = config.has_value() ? &(*config) : nullptr;
|
|
auto result = pc()->CreateDataChannelOrError(label, config_ptr);
|
|
if (!result.ok()) {
|
|
RTC_LOG(LS_ERROR) << "CreateDataChannel failed: "
|
|
<< ToString(result.error().type()) << " "
|
|
<< result.error().message();
|
|
return nullptr;
|
|
}
|
|
return result.MoveValue();
|
|
}
|
|
|
|
PeerConnectionInterface::SignalingState
|
|
PeerConnectionWrapper::signaling_state() {
|
|
return pc()->signaling_state();
|
|
}
|
|
|
|
bool PeerConnectionWrapper::IsIceGatheringDone() {
|
|
return observer()->ice_gathering_complete_;
|
|
}
|
|
|
|
bool PeerConnectionWrapper::IsIceConnected() {
|
|
return observer()->ice_connected_;
|
|
}
|
|
|
|
rtc::scoped_refptr<const RTCStatsReport> PeerConnectionWrapper::GetStats() {
|
|
auto callback = rtc::make_ref_counted<MockRTCStatsCollectorCallback>();
|
|
pc()->GetStats(callback.get());
|
|
EXPECT_THAT(
|
|
WaitUntil([&] { return callback->called(); }, ::testing::IsTrue()),
|
|
IsRtcOk());
|
|
return callback->report();
|
|
}
|
|
|
|
} // namespace webrtc
|