Use new TransportController implementation in PeerConnection.

The TransportController will be replaced by the JsepTransportController
and JsepTransport will be replace be JsepTransport2.

The JsepTransportController will take the entire SessionDescription
and handle the RtcpMux, Sdes and BUNDLE internally.

The ownership model is also changed. The P2P layer transports are not
ref-counted and will be owned by the JsepTransport2.

In ORTC aspect, RtpTransportAdapter is now a wrapper over RtpTransport
or SrtpTransport and it implements the public and internal interface
by calling the transport underneath.

Bug: webrtc:8587
Change-Id: Ia7fa61288a566f211f8560072ea0eecaf19e48df
Reviewed-on: https://webrtc-review.googlesource.com/59586
Commit-Queue: Zhi Huang <zhihuang@webrtc.org>
Reviewed-by: Taylor Brandstetter <deadbeef@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#22693}
This commit is contained in:
Zhi Huang 2018-03-30 10:48:35 -07:00 committed by Commit Bot
parent 80e9339e39
commit e830e683c4
51 changed files with 1604 additions and 6969 deletions

View File

@ -31,6 +31,7 @@ class PacketTransportInterface {
// Classes that can use this internal interface.
friend class RtpTransportControllerAdapter;
friend class RtpTransportAdapter;
};
} // namespace webrtc

View File

@ -232,7 +232,7 @@ OrtcFactory::CreateRtpTransportController() {
RTC_DCHECK_RUN_ON(signaling_thread_);
return RtpTransportControllerAdapter::CreateProxied(
cricket::MediaConfig(), channel_manager_.get(), null_event_log_.get(),
signaling_thread_, worker_thread_.get());
signaling_thread_, worker_thread_.get(), network_thread_);
}
RTCErrorOr<std::unique_ptr<RtpTransportInterface>>

View File

@ -17,6 +17,7 @@
#include "api/proxy.h"
#include "rtc_base/logging.h"
#include "rtc_base/ptr_util.h"
namespace webrtc {
@ -115,10 +116,11 @@ RtpTransportAdapter::CreateSrtpProxied(
LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
"Must provide an RTP transport controller.");
}
std::unique_ptr<RtpTransportAdapter> transport_adapter(
new RtpTransportAdapter(parameters.rtcp, rtp, rtcp,
rtp_transport_controller,
true /*is_srtp_transport*/));
std::unique_ptr<RtpTransportAdapter> transport_adapter;
transport_adapter.reset(new RtpTransportAdapter(parameters.rtcp, rtp, rtcp,
rtp_transport_controller,
true /*is_srtp_transport*/));
RTCError params_result = transport_adapter->SetParameters(parameters);
if (!params_result.ok()) {
return std::move(params_result);
@ -145,25 +147,40 @@ RtpTransportAdapter::RtpTransportAdapter(
: rtp_packet_transport_(rtp),
rtcp_packet_transport_(rtcp),
rtp_transport_controller_(rtp_transport_controller),
is_srtp_transport_(is_srtp_transport) {
network_thread_(rtp_transport_controller_->network_thread()) {
parameters_.rtcp = rtcp_params;
// CNAME should have been filled by OrtcFactory if empty.
RTC_DCHECK(!parameters_.rtcp.cname.empty());
RTC_DCHECK(rtp_transport_controller);
if (is_srtp_transport) {
srtp_transport_ = rtc::MakeUnique<SrtpTransport>(rtcp == nullptr);
transport_ = srtp_transport_.get();
} else {
unencrypted_rtp_transport_ = rtc::MakeUnique<RtpTransport>(rtcp == nullptr);
transport_ = unencrypted_rtp_transport_.get();
}
RTC_DCHECK(transport_);
network_thread_->Invoke<void>(RTC_FROM_HERE, [=] {
SetRtpPacketTransport(rtp->GetInternal());
if (rtcp) {
SetRtcpPacketTransport(rtcp->GetInternal());
}
});
transport_->SignalReadyToSend.connect(this,
&RtpTransportAdapter::OnReadyToSend);
transport_->SignalPacketReceived.connect(
this, &RtpTransportAdapter::OnPacketReceived);
transport_->SignalWritableState.connect(
this, &RtpTransportAdapter::OnWritableState);
}
RtpTransportAdapter::~RtpTransportAdapter() {
SignalDestroyed(this);
}
PacketTransportInterface* RtpTransportAdapter::GetRtpPacketTransport() const {
return rtp_packet_transport_;
}
PacketTransportInterface* RtpTransportAdapter::GetRtcpPacketTransport() const {
return rtcp_packet_transport_;
}
RTCError RtpTransportAdapter::SetParameters(
const RtpTransportParameters& parameters) {
if (!parameters.rtcp.mux && parameters_.rtcp.mux) {
@ -194,34 +211,20 @@ RTCError RtpTransportAdapter::SetParameters(
RTCError RtpTransportAdapter::SetSrtpSendKey(
const cricket::CryptoParams& params) {
if (send_key_) {
LOG_AND_RETURN_ERROR(
webrtc::RTCErrorType::UNSUPPORTED_OPERATION,
"Setting the SRTP send key twice is currently unsupported.");
if (!network_thread_->IsCurrent()) {
return network_thread_->Invoke<RTCError>(
RTC_FROM_HERE, [&] { return SetSrtpSendKey(params); });
}
if (receive_key_ && receive_key_->cipher_suite != params.cipher_suite) {
LOG_AND_RETURN_ERROR(
webrtc::RTCErrorType::UNSUPPORTED_OPERATION,
"The send key and receive key must have the same cipher suite.");
}
send_key_ = params;
return RTCError::OK();
return transport_->SetSrtpSendKey(params);
}
RTCError RtpTransportAdapter::SetSrtpReceiveKey(
const cricket::CryptoParams& params) {
if (receive_key_) {
LOG_AND_RETURN_ERROR(
webrtc::RTCErrorType::UNSUPPORTED_OPERATION,
"Setting the SRTP receive key twice is currently unsupported.");
if (!network_thread_->IsCurrent()) {
return network_thread_->Invoke<RTCError>(
RTC_FROM_HERE, [&] { return SetSrtpReceiveKey(params); });
}
if (send_key_ && send_key_->cipher_suite != params.cipher_suite) {
LOG_AND_RETURN_ERROR(
webrtc::RTCErrorType::UNSUPPORTED_OPERATION,
"The send key and receive key must have the same cipher suite.");
}
receive_key_ = params;
return RTCError::OK();
return transport_->SetSrtpReceiveKey(params);
}
} // namespace webrtc

View File

@ -19,19 +19,25 @@
#include "media/base/streamparams.h"
#include "ortc/rtptransportcontrolleradapter.h"
#include "pc/channel.h"
#include "pc/rtptransport.h"
#include "pc/rtptransportinternal.h"
#include "pc/srtptransport.h"
#include "rtc_base/constructormagic.h"
#include "rtc_base/sigslot.h"
namespace webrtc {
// Implementation of SrtpTransportInterface to be used with RtpSenderAdapter,
// RtpReceiverAdapter, and RtpTransportControllerAdapter classes. This class
// is used to implement both a secure and insecure RTP transport.
// This class is a wrapper over an RtpTransport or an SrtpTransport. The base
// class RtpTransportInternalAdapter keeps a raw pointer, |transport_|, of the
// transport object and implements both the public SrtpTransportInterface and
// RtpTransport internal interface by calling the |transport_| underneath.
//
// This adapter can be used as an unencrypted RTP transport or an SrtpTransport
// with RtpSenderAdapter, RtpReceiverAdapter, and RtpTransportControllerAdapter.
//
// TODO(deadbeef): When BaseChannel is split apart into separate
// "RtpTransport"/"RtpTransceiver"/"RtpSender"/"RtpReceiver" objects, this
// adapter object can be removed.
class RtpTransportAdapter : public SrtpTransportInterface {
class RtpTransportAdapter : public RtpTransportInternalAdapter {
public:
// |rtp| can't be null. |rtcp| can if RTCP muxing is used immediately (meaning
// |rtcp_parameters.mux| is also true).
@ -50,8 +56,12 @@ class RtpTransportAdapter : public SrtpTransportInterface {
~RtpTransportAdapter() override;
// RtpTransportInterface implementation.
PacketTransportInterface* GetRtpPacketTransport() const override;
PacketTransportInterface* GetRtcpPacketTransport() const override;
PacketTransportInterface* GetRtpPacketTransport() const override {
return rtp_packet_transport_;
}
PacketTransportInterface* GetRtcpPacketTransport() const override {
return rtcp_packet_transport_;
}
RTCError SetParameters(const RtpTransportParameters& parameters) override;
RtpTransportParameters GetParameters() const override { return parameters_; }
@ -70,13 +80,9 @@ class RtpTransportAdapter : public SrtpTransportInterface {
// returning this transport from GetTransports().
sigslot::signal1<RtpTransportAdapter*> SignalDestroyed;
// Used by the RtpTransportControllerAdapter to tell if an rtp sender or
// receiver can be created.
bool is_srtp_transport() { return is_srtp_transport_; }
// Used by the RtpTransportControllerAdapter to set keys for senders and
// receivers.
rtc::Optional<cricket::CryptoParams> send_key() { return send_key_; }
rtc::Optional<cricket::CryptoParams> receive_key() { return receive_key_; }
bool IsSrtpActive() const override { return transport_->IsSrtpActive(); }
bool IsSrtpTransport() const { return srtp_transport_ != nullptr; }
protected:
RtpTransportAdapter* GetInternal() override { return this; }
@ -88,18 +94,29 @@ class RtpTransportAdapter : public SrtpTransportInterface {
RtpTransportControllerAdapter* rtp_transport_controller,
bool is_srtp_transport);
PacketTransportInterface* rtp_packet_transport_;
PacketTransportInterface* rtcp_packet_transport_;
RtpTransportControllerAdapter* const rtp_transport_controller_;
void OnReadyToSend(bool ready) { SignalReadyToSend(ready); }
void OnPacketReceived(bool rtcp,
rtc::CopyOnWriteBuffer* packet,
const rtc::PacketTime& time) {
SignalPacketReceived(rtcp, packet, time);
}
void OnWritableState(bool writable) { SignalWritableState(writable); }
PacketTransportInterface* rtp_packet_transport_ = nullptr;
PacketTransportInterface* rtcp_packet_transport_ = nullptr;
RtpTransportControllerAdapter* const rtp_transport_controller_ = nullptr;
// Non-null if this class owns the transport controller.
std::unique_ptr<RtpTransportControllerInterface>
owned_rtp_transport_controller_;
RtpTransportParameters parameters_;
// SRTP specific members.
rtc::Optional<cricket::CryptoParams> send_key_;
rtc::Optional<cricket::CryptoParams> receive_key_;
bool is_srtp_transport_;
// Only one of them is non-null;
std::unique_ptr<RtpTransport> unencrypted_rtp_transport_;
std::unique_ptr<SrtpTransport> srtp_transport_;
rtc::Thread* network_thread_ = nullptr;
RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(RtpTransportAdapter);
};

View File

@ -185,6 +185,7 @@ TEST_F(RtpTransportControllerTest,
auto video_sender_result = ortc_factory_->CreateRtpSender(
cricket::MEDIA_TYPE_VIDEO, rtp_transport2.get());
EXPECT_TRUE(video_sender_result.ok());
auto video_receiver_result = ortc_factory_->CreateRtpReceiver(
cricket::MEDIA_TYPE_VIDEO, rtp_transport1.get());
EXPECT_EQ(RTCErrorType::UNSUPPORTED_OPERATION,

View File

@ -97,10 +97,12 @@ RtpTransportControllerAdapter::CreateProxied(
cricket::ChannelManager* channel_manager,
webrtc::RtcEventLog* event_log,
rtc::Thread* signaling_thread,
rtc::Thread* worker_thread) {
rtc::Thread* worker_thread,
rtc::Thread* network_thread) {
std::unique_ptr<RtpTransportControllerAdapter> wrapped(
new RtpTransportControllerAdapter(config, channel_manager, event_log,
signaling_thread, worker_thread));
signaling_thread, worker_thread,
network_thread));
return RtpTransportControllerProxyWithInternal<
RtpTransportControllerAdapter>::Create(signaling_thread, worker_thread,
std::move(wrapped));
@ -610,9 +612,11 @@ RtpTransportControllerAdapter::RtpTransportControllerAdapter(
cricket::ChannelManager* channel_manager,
webrtc::RtcEventLog* event_log,
rtc::Thread* signaling_thread,
rtc::Thread* worker_thread)
rtc::Thread* worker_thread,
rtc::Thread* network_thread)
: signaling_thread_(signaling_thread),
worker_thread_(worker_thread),
network_thread_(network_thread),
media_config_(config),
channel_manager_(channel_manager),
event_log_(event_log),
@ -678,11 +682,7 @@ RTCError RtpTransportControllerAdapter::AttachAudioSender(
"RtpSender and RtpReceiver is not currently "
"supported.");
}
RTCError err = MaybeSetCryptos(inner_transport, &local_audio_description_,
&remote_audio_description_);
if (!err.ok()) {
return err;
}
// If setting new transport, extract its RTCP parameters and create voice
// channel.
if (!inner_audio_transport_) {
@ -713,11 +713,7 @@ RTCError RtpTransportControllerAdapter::AttachVideoSender(
"RtpSender and RtpReceiver is not currently "
"supported.");
}
RTCError err = MaybeSetCryptos(inner_transport, &local_video_description_,
&remote_video_description_);
if (!err.ok()) {
return err;
}
// If setting new transport, extract its RTCP parameters and create video
// channel.
if (!inner_video_transport_) {
@ -748,11 +744,7 @@ RTCError RtpTransportControllerAdapter::AttachAudioReceiver(
"RtpReceiver and RtpReceiver is not currently "
"supported.");
}
RTCError err = MaybeSetCryptos(inner_transport, &local_audio_description_,
&remote_audio_description_);
if (!err.ok()) {
return err;
}
// If setting new transport, extract its RTCP parameters and create voice
// channel.
if (!inner_audio_transport_) {
@ -783,11 +775,6 @@ RTCError RtpTransportControllerAdapter::AttachVideoReceiver(
"RtpReceiver and RtpReceiver is not currently "
"supported.");
}
RTCError err = MaybeSetCryptos(inner_transport, &local_video_description_,
&remote_video_description_);
if (!err.ok()) {
return err;
}
// If setting new transport, extract its RTCP parameters and create video
// channel.
if (!inner_video_transport_) {
@ -877,26 +864,24 @@ void RtpTransportControllerAdapter::OnVideoReceiverDestroyed() {
void RtpTransportControllerAdapter::CreateVoiceChannel() {
voice_channel_ = channel_manager_->CreateVoiceChannel(
call_.get(), media_config_,
inner_audio_transport_->GetRtpPacketTransport()->GetInternal(),
inner_audio_transport_->GetRtcpPacketTransport()
? inner_audio_transport_->GetRtcpPacketTransport()->GetInternal()
: nullptr,
signaling_thread_, "audio", false, cricket::AudioOptions());
call_.get(), media_config_, inner_audio_transport_->GetInternal(),
signaling_thread_, "audio", false, rtc::CryptoOptions(),
cricket::AudioOptions());
RTC_DCHECK(voice_channel_);
voice_channel_->Enable(true);
voice_channel_->DisableEncryption(
!inner_audio_transport_->GetInternal()->IsSrtpTransport());
}
void RtpTransportControllerAdapter::CreateVideoChannel() {
video_channel_ = channel_manager_->CreateVideoChannel(
call_.get(), media_config_,
inner_video_transport_->GetRtpPacketTransport()->GetInternal(),
inner_video_transport_->GetRtcpPacketTransport()
? inner_video_transport_->GetRtcpPacketTransport()->GetInternal()
: nullptr,
signaling_thread_, "video", false, cricket::VideoOptions());
call_.get(), media_config_, inner_video_transport_->GetInternal(),
signaling_thread_, "video", false, rtc::CryptoOptions(),
cricket::VideoOptions());
RTC_DCHECK(video_channel_);
video_channel_->Enable(true);
video_channel_->DisableEncryption(
!inner_video_transport_->GetInternal()->IsSrtpTransport());
}
void RtpTransportControllerAdapter::DestroyVoiceChannel() {
@ -989,25 +974,4 @@ RtpTransportControllerAdapter::MakeSendStreamParamsVec(
return result;
}
RTCError RtpTransportControllerAdapter::MaybeSetCryptos(
RtpTransportInterface* rtp_transport,
cricket::MediaContentDescription* local_description,
cricket::MediaContentDescription* remote_description) {
if (rtp_transport->GetInternal()->is_srtp_transport()) {
if (!rtp_transport->GetInternal()->send_key() ||
!rtp_transport->GetInternal()->receive_key()) {
LOG_AND_RETURN_ERROR(webrtc::RTCErrorType::UNSUPPORTED_PARAMETER,
"The SRTP send key or receive key is not set.")
}
std::vector<cricket::CryptoParams> cryptos;
cryptos.push_back(*(rtp_transport->GetInternal()->receive_key()));
local_description->set_cryptos(cryptos);
cryptos.clear();
cryptos.push_back(*(rtp_transport->GetInternal()->send_key()));
remote_description->set_cryptos(cryptos);
}
return RTCError::OK();
}
} // namespace webrtc

View File

@ -66,7 +66,8 @@ class RtpTransportControllerAdapter : public RtpTransportControllerInterface,
cricket::ChannelManager* channel_manager,
webrtc::RtcEventLog* event_log,
rtc::Thread* signaling_thread,
rtc::Thread* worker_thread);
rtc::Thread* worker_thread,
rtc::Thread* network_thread);
~RtpTransportControllerAdapter() override;
@ -100,6 +101,7 @@ class RtpTransportControllerAdapter : public RtpTransportControllerInterface,
// Methods used internally by other "adapter" classes.
rtc::Thread* signaling_thread() const { return signaling_thread_; }
rtc::Thread* worker_thread() const { return worker_thread_; }
rtc::Thread* network_thread() const { return network_thread_; }
// |parameters.keepalive| will be set for ALL RTP transports in the call.
RTCError SetRtpTransportParameters(const RtpTransportParameters& parameters,
@ -131,7 +133,8 @@ class RtpTransportControllerAdapter : public RtpTransportControllerInterface,
cricket::ChannelManager* channel_manager,
webrtc::RtcEventLog* event_log,
rtc::Thread* signaling_thread,
rtc::Thread* worker_thread);
rtc::Thread* worker_thread,
rtc::Thread* network_thread);
void Init_w();
void Close_w();
@ -180,15 +183,9 @@ class RtpTransportControllerAdapter : public RtpTransportControllerInterface,
const std::string& cname,
const cricket::MediaContentDescription& description) const;
// If the |rtp_transport| is a SrtpTransport, set the cryptos of the
// audio/video content descriptions.
RTCError MaybeSetCryptos(
RtpTransportInterface* rtp_transport,
cricket::MediaContentDescription* local_description,
cricket::MediaContentDescription* remote_description);
rtc::Thread* signaling_thread_;
rtc::Thread* worker_thread_;
rtc::Thread* network_thread_;
// |transport_proxies_| and |inner_audio_transport_|/|inner_audio_transport_|
// are somewhat redundant, but the latter are only set when
// RtpSenders/RtpReceivers are attached to the transport.

View File

@ -122,49 +122,4 @@ TEST_F(SrtpTransportTest, SetSrtpSendAndReceiveKeyDifferentCipherSuite) {
EXPECT_TRUE(receiver_result.ok());
}
class SrtpTransportTestWithMediaType
: public SrtpTransportTest,
public ::testing::WithParamInterface<cricket::MediaType> {};
// Tests that the senders cannot be created before setting the keys.
TEST_P(SrtpTransportTestWithMediaType, CreateSenderBeforeSettingKeys) {
auto sender_result =
ortc_factory_->CreateRtpSender(GetParam(), srtp_transport_.get());
EXPECT_EQ(RTCErrorType::UNSUPPORTED_PARAMETER, sender_result.error().type());
EXPECT_TRUE(srtp_transport_->SetSrtpSendKey(kTestSha1CryptoParams1).ok());
sender_result =
ortc_factory_->CreateRtpSender(GetParam(), srtp_transport_.get());
EXPECT_EQ(RTCErrorType::UNSUPPORTED_PARAMETER, sender_result.error().type());
EXPECT_TRUE(srtp_transport_->SetSrtpReceiveKey(kTestSha1CryptoParams2).ok());
// Ensure that after the keys are set, a sender can be created, despite the
// previous errors.
sender_result =
ortc_factory_->CreateRtpSender(GetParam(), srtp_transport_.get());
EXPECT_TRUE(sender_result.ok());
}
// Tests that the receivers cannot be created before setting the keys.
TEST_P(SrtpTransportTestWithMediaType, CreateReceiverBeforeSettingKeys) {
auto receiver_result =
ortc_factory_->CreateRtpReceiver(GetParam(), srtp_transport_.get());
EXPECT_EQ(RTCErrorType::UNSUPPORTED_PARAMETER,
receiver_result.error().type());
EXPECT_TRUE(srtp_transport_->SetSrtpSendKey(kTestSha1CryptoParams1).ok());
receiver_result =
ortc_factory_->CreateRtpReceiver(GetParam(), srtp_transport_.get());
EXPECT_EQ(RTCErrorType::UNSUPPORTED_PARAMETER,
receiver_result.error().type());
EXPECT_TRUE(srtp_transport_->SetSrtpReceiveKey(kTestSha1CryptoParams2).ok());
// Ensure that after the keys are set, a receiver can be created, despite the
// previous errors.
receiver_result =
ortc_factory_->CreateRtpReceiver(GetParam(), srtp_transport_.get());
EXPECT_TRUE(receiver_result.ok());
}
INSTANTIATE_TEST_CASE_P(SenderCreatationTest,
SrtpTransportTestWithMediaType,
::testing::Values(cricket::MEDIA_TYPE_AUDIO,
cricket::MEDIA_TYPE_VIDEO));
} // namespace webrtc

View File

@ -42,8 +42,6 @@ rtc_static_library("rtc_pc_base") {
"dtlssrtptransport.h",
"externalhmac.cc",
"externalhmac.h",
"jseptransport.cc",
"jseptransport.h",
"jseptransport2.cc",
"jseptransport2.h",
"jseptransportcontroller.cc",
@ -66,8 +64,6 @@ rtc_static_library("rtc_pc_base") {
"srtpsession.h",
"srtptransport.cc",
"srtptransport.h",
"transportcontroller.cc",
"transportcontroller.h",
"transportstats.cc",
"transportstats.h",
]
@ -280,7 +276,6 @@ if (rtc_include_tests) {
"currentspeakermonitor_unittest.cc",
"dtlssrtptransport_unittest.cc",
"jseptransport2_unittest.cc",
"jseptransport_unittest.cc",
"jseptransportcontroller_unittest.cc",
"mediasession_unittest.cc",
"rtcpmuxfilter_unittest.cc",
@ -290,7 +285,6 @@ if (rtc_include_tests) {
"srtpsession_unittest.cc",
"srtptestutil.h",
"srtptransport_unittest.cc",
"transportcontroller_unittest.cc",
]
include_dirs = [ "//third_party/libsrtp/srtp" ]
@ -350,7 +344,6 @@ if (rtc_include_tests) {
"test/fakeperiodicvideosource.h",
"test/fakertccertificategenerator.h",
"test/fakesctptransport.h",
"test/faketransportcontroller.h",
"test/fakevideotrackrenderer.h",
"test/fakevideotracksource.h",
"test/mock_datachannel.h",

View File

@ -98,20 +98,16 @@ BaseChannel::BaseChannel(rtc::Thread* worker_thread,
rtc::Thread* signaling_thread,
std::unique_ptr<MediaChannel> media_channel,
const std::string& content_name,
bool rtcp_mux_required,
bool srtp_required)
bool srtp_required,
rtc::CryptoOptions crypto_options)
: worker_thread_(worker_thread),
network_thread_(network_thread),
signaling_thread_(signaling_thread),
content_name_(content_name),
rtcp_mux_required_(rtcp_mux_required),
unencrypted_rtp_transport_(
rtc::MakeUnique<webrtc::RtpTransport>(rtcp_mux_required)),
srtp_required_(srtp_required),
crypto_options_(crypto_options),
media_channel_(std::move(media_channel)) {
RTC_DCHECK_RUN_ON(worker_thread_);
rtp_transport_ = unencrypted_rtp_transport_.get();
ConnectToRtpTransport();
RTC_LOG(LS_INFO) << "Created channel for " << content_name;
}
@ -162,33 +158,10 @@ void BaseChannel::DisconnectFromRtpTransport() {
rtp_transport_->SetMetricsObserver(nullptr);
}
void BaseChannel::Init_w(DtlsTransportInternal* rtp_dtls_transport,
DtlsTransportInternal* rtcp_dtls_transport,
rtc::PacketTransportInternal* rtp_packet_transport,
rtc::PacketTransportInternal* rtcp_packet_transport) {
RTC_DCHECK_RUN_ON(worker_thread_);
network_thread_->Invoke<void>(RTC_FROM_HERE, [&] {
SetTransports_n(rtp_dtls_transport, rtcp_dtls_transport,
rtp_packet_transport, rtcp_packet_transport);
if (rtcp_mux_required_) {
rtcp_mux_filter_.SetActive();
}
});
// Both RTP and RTCP channels should be set, we can call SetInterface on
// the media channel and it can set network options.
media_channel_->SetInterface(this);
}
void BaseChannel::Init_w(webrtc::RtpTransportInternal* rtp_transport) {
RTC_DCHECK_RUN_ON(worker_thread_);
network_thread_->Invoke<void>(RTC_FROM_HERE, [&] {
SetRtpTransport(rtp_transport);
if (rtcp_mux_required_) {
rtcp_mux_filter_.SetActive();
}
});
// Both RTP and RTCP channels should be set, we can call SetInterface on
@ -205,11 +178,8 @@ void BaseChannel::Deinit() {
network_thread_->Invoke<void>(RTC_FROM_HERE, [&] {
FlushRtcpMessages_n();
if (dtls_srtp_transport_) {
dtls_srtp_transport_->SetDtlsTransports(nullptr, nullptr);
} else {
rtp_transport_->SetRtpPacketTransport(nullptr);
rtp_transport_->SetRtcpPacketTransport(nullptr);
if (rtp_transport_) {
DisconnectFromRtpTransport();
}
// Clear pending read packets/messages.
network_thread_->Clear(&invoker_);
@ -221,143 +191,34 @@ void BaseChannel::SetRtpTransport(webrtc::RtpTransportInternal* rtp_transport) {
if (!network_thread_->IsCurrent()) {
network_thread_->Invoke<void>(RTC_FROM_HERE, [&] {
SetRtpTransport(rtp_transport);
return;
});
return;
}
RTC_DCHECK(rtp_transport);
if (rtp_transport_) {
DisconnectFromRtpTransport();
}
rtp_transport_ = rtp_transport;
RTC_LOG(LS_INFO) << "Setting the RtpTransport for " << content_name();
ConnectToRtpTransport();
if (rtp_transport_) {
RTC_DCHECK(rtp_transport_->rtp_packet_transport());
transport_name_ = rtp_transport_->rtp_packet_transport()->transport_name();
UpdateWritableState_n();
}
ConnectToRtpTransport();
OnTransportReadyToSend(rtp_transport_->IsReadyToSend());
UpdateWritableState_n();
void BaseChannel::SetTransports(DtlsTransportInternal* rtp_dtls_transport,
DtlsTransportInternal* rtcp_dtls_transport) {
network_thread_->Invoke<void>(
RTC_FROM_HERE,
Bind(&BaseChannel::SetTransports_n, this, rtp_dtls_transport,
rtcp_dtls_transport, rtp_dtls_transport, rtcp_dtls_transport));
}
void BaseChannel::SetTransports(
rtc::PacketTransportInternal* rtp_packet_transport,
rtc::PacketTransportInternal* rtcp_packet_transport) {
network_thread_->Invoke<void>(
RTC_FROM_HERE, Bind(&BaseChannel::SetTransports_n, this, nullptr, nullptr,
rtp_packet_transport, rtcp_packet_transport));
}
void BaseChannel::SetTransports_n(
DtlsTransportInternal* rtp_dtls_transport,
DtlsTransportInternal* rtcp_dtls_transport,
rtc::PacketTransportInternal* rtp_packet_transport,
rtc::PacketTransportInternal* rtcp_packet_transport) {
RTC_DCHECK(network_thread_->IsCurrent());
// Validate some assertions about the input.
RTC_DCHECK(rtp_packet_transport);
RTC_DCHECK_EQ(NeedsRtcpTransport(), rtcp_packet_transport != nullptr);
if (rtp_dtls_transport || rtcp_dtls_transport) {
// DTLS/non-DTLS pointers should be to the same object.
RTC_DCHECK(rtp_dtls_transport == rtp_packet_transport);
RTC_DCHECK(rtcp_dtls_transport == rtcp_packet_transport);
// Can't go from non-DTLS to DTLS.
RTC_DCHECK(!rtp_transport_->rtp_packet_transport() || rtp_dtls_transport_);
} else {
// Can't go from DTLS to non-DTLS.
RTC_DCHECK(!rtp_dtls_transport_);
}
// Transport names should be the same.
if (rtp_dtls_transport && rtcp_dtls_transport) {
RTC_DCHECK(rtp_dtls_transport->transport_name() ==
rtcp_dtls_transport->transport_name());
}
if (rtp_packet_transport == rtp_transport_->rtp_packet_transport()) {
// Nothing to do if transport isn't changing.
return;
}
std::string debug_name;
if (rtp_dtls_transport) {
transport_name_ = rtp_dtls_transport->transport_name();
debug_name = transport_name_;
} else {
debug_name = rtp_packet_transport->transport_name();
}
// If this BaseChannel doesn't require RTCP mux and we haven't fully
// negotiated RTCP mux, we need an RTCP transport.
if (rtcp_packet_transport) {
RTC_LOG(LS_INFO) << "Setting RTCP Transport for " << content_name()
<< " on " << debug_name << " transport "
<< rtcp_packet_transport;
SetTransport_n(/*rtcp=*/true, rtcp_dtls_transport, rtcp_packet_transport);
}
RTC_LOG(LS_INFO) << "Setting RTP Transport for " << content_name() << " on "
<< debug_name << " transport " << rtp_packet_transport;
SetTransport_n(/*rtcp=*/false, rtp_dtls_transport, rtp_packet_transport);
// Set DtlsTransport/PacketTransport for RTP-level transport.
if ((rtp_dtls_transport_ || rtcp_dtls_transport_) && dtls_srtp_transport_) {
// When setting the transport with non-null |dtls_srtp_transport_|, we are
// using DTLS-SRTP. This could happen for bundling. If the
// |dtls_srtp_transport| is null, we cannot tell if it doing DTLS-SRTP or
// SDES until the description is set. So don't call |EnableDtlsSrtp_n| here.
dtls_srtp_transport_->SetDtlsTransports(rtp_dtls_transport,
rtcp_dtls_transport);
} else {
rtp_transport_->SetRtpPacketTransport(rtp_packet_transport);
rtp_transport_->SetRtcpPacketTransport(rtcp_packet_transport);
}
// Update aggregate writable/ready-to-send state between RTP and RTCP upon
// setting new transport channels.
UpdateWritableState_n();
}
void BaseChannel::SetTransport_n(
bool rtcp,
DtlsTransportInternal* new_dtls_transport,
rtc::PacketTransportInternal* new_packet_transport) {
RTC_DCHECK(network_thread_->IsCurrent());
if (new_dtls_transport) {
RTC_DCHECK(new_dtls_transport == new_packet_transport);
}
DtlsTransportInternal*& old_dtls_transport =
rtcp ? rtcp_dtls_transport_ : rtp_dtls_transport_;
rtc::PacketTransportInternal* old_packet_transport =
rtcp ? rtp_transport_->rtcp_packet_transport()
: rtp_transport_->rtp_packet_transport();
if (!old_packet_transport && !new_packet_transport) {
// Nothing to do.
return;
}
RTC_DCHECK(old_packet_transport != new_packet_transport);
old_dtls_transport = new_dtls_transport;
// If there's no new transport, we're done.
if (!new_packet_transport) {
return;
}
if (rtcp && new_dtls_transport) {
RTC_CHECK(!(ShouldSetupDtlsSrtp_n() && srtp_active()))
<< "Setting RTCP for DTLS/SRTP after the DTLS is active "
<< "should never happen.";
}
auto& socket_options = rtcp ? rtcp_socket_options_ : socket_options_;
for (const auto& pair : socket_options) {
new_packet_transport->SetOption(pair.first, pair.second);
// Set the cached socket options.
for (const auto& pair : socket_options_) {
rtp_transport_->rtp_packet_transport()->SetOption(pair.first,
pair.second);
}
if (rtp_transport_->rtcp_packet_transport()) {
for (const auto& pair : rtcp_socket_options_) {
rtp_transport_->rtp_packet_transport()->SetOption(pair.first,
pair.second);
}
}
}
}
@ -416,12 +277,6 @@ bool BaseChannel::SetRemoteContent(const MediaContentDescription* content,
Bind(&BaseChannel::SetRemoteContent_w, this, content, type, error_desc));
}
bool BaseChannel::NeedsRtcpTransport() {
// If this BaseChannel doesn't require RTCP mux and we haven't fully
// negotiated RTCP mux, we need an RTCP transport.
return !rtcp_mux_required_ && !rtcp_mux_filter_.IsFullyActive();
}
bool BaseChannel::IsReadyToReceiveMedia_w() const {
// Receive data if we are enabled and have local content,
return enabled() &&
@ -440,7 +295,7 @@ bool BaseChannel::IsReadyToSendMedia_n() const {
return enabled() &&
webrtc::RtpTransceiverDirectionHasRecv(remote_content_direction_) &&
webrtc::RtpTransceiverDirectionHasSend(local_content_direction_) &&
was_ever_writable() && (srtp_active() || !ShouldSetupDtlsSrtp_n());
was_ever_writable() && (srtp_active() || encryption_disabled_);
}
bool BaseChannel::SendPacket(rtc::CopyOnWriteBuffer* packet,
@ -463,6 +318,7 @@ int BaseChannel::SetOption_n(SocketType type,
rtc::Socket::Option opt,
int value) {
RTC_DCHECK(network_thread_->IsCurrent());
RTC_DCHECK(rtp_transport_);
rtc::PacketTransportInternal* transport = nullptr;
switch (type) {
case ST_RTP:
@ -482,11 +338,6 @@ int BaseChannel::SetOption_n(SocketType type,
void BaseChannel::OnWritableState(bool writable) {
RTC_DCHECK(network_thread_->IsCurrent());
if (writable) {
// This is used to cover the scenario when the DTLS handshake is completed
// and DtlsTransport becomes writable before the remote description is set.
if (ShouldSetupDtlsSrtp_n()) {
EnableDtlsSrtp_n();
}
ChannelWritable_n();
} else {
ChannelNotWritable_n();
@ -533,13 +384,14 @@ bool BaseChannel::SendPacket(bool rtcp,
network_thread_->Post(RTC_FROM_HERE, this, message_id, data);
return true;
}
TRACE_EVENT0("webrtc", "BaseChannel::SendPacket");
// Now that we are on the correct thread, ensure we have a place to send this
// packet before doing anything. (We might get RTCP packets that we don't
// intend to send.) If we've negotiated RTCP mux, send RTCP over the RTP
// transport.
if (!rtp_transport_->IsWritable(rtcp)) {
if (!rtp_transport_ || !rtp_transport_->IsWritable(rtcp)) {
return false;
}
@ -571,18 +423,15 @@ bool BaseChannel::SendPacket(bool rtcp,
std::string packet_type = rtcp ? "RTCP" : "RTP";
RTC_LOG(LS_WARNING) << "Sending an " << packet_type
<< " packet without encryption.";
} else {
// Make sure we didn't accidentally send any packets without encryption.
RTC_DCHECK(rtp_transport_ == sdes_transport_.get() ||
rtp_transport_ == dtls_srtp_transport_.get());
}
// Bon voyage.
return rtcp ? rtp_transport_->SendRtcpPacket(packet, options, PF_SRTP_BYPASS)
: rtp_transport_->SendRtpPacket(packet, options, PF_SRTP_BYPASS);
}
bool BaseChannel::HandlesPayloadType(int packet_type) const {
return rtp_transport_->HandlesPayloadType(packet_type);
return bundle_filter_.FindPayloadType(packet_type);
}
void BaseChannel::OnPacketReceived(bool rtcp,
@ -593,6 +442,11 @@ void BaseChannel::OnPacketReceived(bool rtcp,
signaling_thread()->Post(RTC_FROM_HERE, this, MSG_FIRSTPACKETRECEIVED);
}
// Filter out the packet this channel cannot handle.
if (!rtcp && !bundle_filter_.DemuxPacket(packet->data(), packet->size())) {
return;
}
if (!srtp_active() && srtp_required_) {
// Our session description indicates that SRTP is required, but we got a
// packet before our SRTP filter is active. This means either that
@ -652,12 +506,8 @@ void BaseChannel::DisableMedia_w() {
}
void BaseChannel::UpdateWritableState_n() {
rtc::PacketTransportInternal* rtp_packet_transport =
rtp_transport_->rtp_packet_transport();
rtc::PacketTransportInternal* rtcp_packet_transport =
rtp_transport_->rtcp_packet_transport();
if (rtp_packet_transport && rtp_packet_transport->writable() &&
(!rtcp_packet_transport || rtcp_packet_transport->writable())) {
if (rtp_transport_->IsWritable(/*rtcp=*/true) &&
rtp_transport_->IsWritable(/*rtcp=*/false)) {
ChannelWritable_n();
} else {
ChannelNotWritable_n();
@ -678,11 +528,6 @@ void BaseChannel::ChannelWritable_n() {
UpdateMediaSendRecvState();
}
bool BaseChannel::ShouldSetupDtlsSrtp_n() const {
// Since DTLS is applied to all transports, checking RTP should be enough.
return rtp_dtls_transport_ && rtp_dtls_transport_->IsDtlsActive();
}
void BaseChannel::ChannelNotWritable_n() {
RTC_DCHECK(network_thread_->IsCurrent());
if (!writable_)
@ -693,238 +538,6 @@ void BaseChannel::ChannelNotWritable_n() {
UpdateMediaSendRecvState();
}
bool BaseChannel::SetRtpTransportParameters(
const MediaContentDescription* content,
SdpType type,
ContentSource src,
const RtpHeaderExtensions& extensions,
std::string* error_desc) {
std::vector<int> encrypted_extension_ids;
for (const webrtc::RtpExtension& extension : extensions) {
if (extension.encrypt) {
RTC_LOG(LS_INFO) << "Using " << (src == CS_LOCAL ? "local" : "remote")
<< " encrypted extension: " << extension.ToString();
encrypted_extension_ids.push_back(extension.id);
}
}
// Cache srtp_required_ for belt and suspenders check on SendPacket
return network_thread_->Invoke<bool>(
RTC_FROM_HERE,
Bind(&BaseChannel::SetRtpTransportParameters_n, this, content, type, src,
encrypted_extension_ids, error_desc));
}
bool BaseChannel::SetRtpTransportParameters_n(
const MediaContentDescription* content,
SdpType type,
ContentSource src,
const std::vector<int>& encrypted_extension_ids,
std::string* error_desc) {
RTC_DCHECK(network_thread_->IsCurrent());
if (!SetSrtp_n(content->cryptos(), type, src, encrypted_extension_ids,
error_desc)) {
return false;
}
if (!SetRtcpMux_n(content->rtcp_mux(), type, src, error_desc)) {
return false;
}
return true;
}
// |dtls| will be set to true if DTLS is active for transport and crypto is
// empty.
bool BaseChannel::CheckSrtpConfig_n(const std::vector<CryptoParams>& cryptos,
bool* dtls,
std::string* error_desc) {
*dtls = rtp_dtls_transport_ && rtp_dtls_transport_->IsDtlsActive();
if (*dtls && !cryptos.empty()) {
SafeSetError("Cryptos must be empty when DTLS is active.", error_desc);
return false;
}
return true;
}
void BaseChannel::EnableSdes_n() {
if (sdes_transport_) {
return;
}
// DtlsSrtpTransport and SrtpTransport shouldn't be enabled at the same
// time.
RTC_DCHECK(!dtls_srtp_transport_);
RTC_DCHECK(unencrypted_rtp_transport_);
sdes_transport_ = rtc::MakeUnique<webrtc::SrtpTransport>(
std::move(unencrypted_rtp_transport_));
#if defined(ENABLE_EXTERNAL_AUTH)
sdes_transport_->EnableExternalAuth();
#endif
SetRtpTransport(sdes_transport_.get());
RTC_LOG(LS_INFO) << "Wrapping RtpTransport in SrtpTransport.";
}
void BaseChannel::EnableDtlsSrtp_n() {
if (dtls_srtp_transport_) {
return;
}
// DtlsSrtpTransport and SrtpTransport shouldn't be enabled at the same
// time.
RTC_DCHECK(!sdes_transport_);
RTC_DCHECK(unencrypted_rtp_transport_);
auto srtp_transport = rtc::MakeUnique<webrtc::SrtpTransport>(
std::move(unencrypted_rtp_transport_));
#if defined(ENABLE_EXTERNAL_AUTH)
srtp_transport->EnableExternalAuth();
#endif
dtls_srtp_transport_ =
rtc::MakeUnique<webrtc::DtlsSrtpTransport>(std::move(srtp_transport));
SetRtpTransport(dtls_srtp_transport_.get());
if (cached_send_extension_ids_) {
dtls_srtp_transport_->UpdateSendEncryptedHeaderExtensionIds(
*cached_send_extension_ids_);
}
if (cached_recv_extension_ids_) {
dtls_srtp_transport_->UpdateRecvEncryptedHeaderExtensionIds(
*cached_recv_extension_ids_);
}
// Set the DtlsTransport and the |dtls_srtp_transport_| will handle the DTLS
// relate signal internally.
RTC_DCHECK(rtp_dtls_transport_);
dtls_srtp_transport_->SetDtlsTransports(rtp_dtls_transport_,
rtcp_dtls_transport_);
RTC_LOG(LS_INFO) << "Wrapping SrtpTransport in DtlsSrtpTransport.";
}
bool BaseChannel::SetSrtp_n(const std::vector<CryptoParams>& cryptos,
SdpType type,
ContentSource src,
const std::vector<int>& encrypted_extension_ids,
std::string* error_desc) {
TRACE_EVENT0("webrtc", "BaseChannel::SetSrtp_w");
bool ret = false;
bool dtls = false;
ret = CheckSrtpConfig_n(cryptos, &dtls, error_desc);
if (!ret) {
return false;
}
// If SRTP was not required, but we're setting a description that uses SDES,
// we need to upgrade to an SrtpTransport.
if (!sdes_transport_ && !dtls && !cryptos.empty()) {
EnableSdes_n();
}
if ((type == SdpType::kAnswer || type == SdpType::kPrAnswer) && dtls) {
EnableDtlsSrtp_n();
}
UpdateEncryptedHeaderExtensionIds(src, encrypted_extension_ids);
if (!dtls) {
switch (type) {
case SdpType::kOffer:
ret = sdes_negotiator_.SetOffer(cryptos, src);
break;
case SdpType::kPrAnswer:
ret = sdes_negotiator_.SetProvisionalAnswer(cryptos, src);
break;
case SdpType::kAnswer:
ret = sdes_negotiator_.SetAnswer(cryptos, src);
break;
default:
break;
}
// If setting an SDES answer succeeded, apply the negotiated parameters
// to the SRTP transport.
if ((type == SdpType::kPrAnswer || type == SdpType::kAnswer) && ret) {
if (sdes_negotiator_.send_cipher_suite() &&
sdes_negotiator_.recv_cipher_suite()) {
RTC_DCHECK(cached_send_extension_ids_);
RTC_DCHECK(cached_recv_extension_ids_);
ret = sdes_transport_->SetRtpParams(
*(sdes_negotiator_.send_cipher_suite()),
sdes_negotiator_.send_key().data(),
static_cast<int>(sdes_negotiator_.send_key().size()),
*(cached_send_extension_ids_),
*(sdes_negotiator_.recv_cipher_suite()),
sdes_negotiator_.recv_key().data(),
static_cast<int>(sdes_negotiator_.recv_key().size()),
*(cached_recv_extension_ids_));
} else {
RTC_LOG(LS_INFO) << "No crypto keys are provided for SDES.";
if (type == SdpType::kAnswer && sdes_transport_) {
// Explicitly reset the |sdes_transport_| if no crypto param is
// provided in the answer. No need to call |ResetParams()| for
// |sdes_negotiator_| because it resets the params inside |SetAnswer|.
sdes_transport_->ResetParams();
}
}
}
}
if (!ret) {
SafeSetError("Failed to setup SRTP.", error_desc);
return false;
}
return true;
}
bool BaseChannel::SetRtcpMux_n(bool enable,
SdpType type,
ContentSource src,
std::string* error_desc) {
// Provide a more specific error message for the RTCP mux "require" policy
// case.
if (rtcp_mux_required_ && !enable) {
SafeSetError(
"rtcpMuxPolicy is 'require', but media description does not "
"contain 'a=rtcp-mux'.",
error_desc);
return false;
}
bool ret = false;
switch (type) {
case SdpType::kOffer:
ret = rtcp_mux_filter_.SetOffer(enable, src);
break;
case SdpType::kPrAnswer:
// This may activate RTCP muxing, but we don't yet destroy the transport
// because the final answer may deactivate it.
ret = rtcp_mux_filter_.SetProvisionalAnswer(enable, src);
break;
case SdpType::kAnswer:
ret = rtcp_mux_filter_.SetAnswer(enable, src);
if (ret && rtcp_mux_filter_.IsActive()) {
ActivateRtcpMux();
}
break;
default:
break;
}
if (!ret) {
SafeSetError("Failed to setup RTCP mux filter.", error_desc);
return false;
}
rtp_transport_->SetRtcpMuxEnabled(rtcp_mux_filter_.IsActive());
// |rtcp_mux_filter_| can be active if |action| is SdpType::kPrAnswer or
// SdpType::kAnswer, but we only want to tear down the RTCP transport if we
// received a final answer.
if (rtcp_mux_filter_.IsActive()) {
// If the RTP transport is already writable, then so are we.
if (rtp_transport_->rtp_packet_transport()->writable()) {
ChannelWritable_n();
}
}
return true;
}
bool BaseChannel::AddRecvStream_w(const StreamParams& sp) {
RTC_DCHECK(worker_thread() == rtc::Thread::Current());
return media_channel()->AddRecvStream(sp);
@ -1008,9 +621,8 @@ bool BaseChannel::UpdateRemoteStreams_w(
RtpHeaderExtensions BaseChannel::GetFilteredRtpHeaderExtensions(
const RtpHeaderExtensions& extensions) {
if (!rtp_dtls_transport_ ||
!rtp_dtls_transport_->crypto_options()
.enable_encrypted_rtp_header_extensions) {
RTC_DCHECK(rtp_transport_);
if (crypto_options_.enable_encrypted_rtp_header_extensions) {
RtpHeaderExtensions filtered;
auto pred = [](const webrtc::RtpExtension& extension) {
return !extension.encrypt;
@ -1023,39 +635,6 @@ RtpHeaderExtensions BaseChannel::GetFilteredRtpHeaderExtensions(
return webrtc::RtpExtension::FilterDuplicateNonEncrypted(extensions);
}
void BaseChannel::MaybeCacheRtpAbsSendTimeHeaderExtension_w(
const std::vector<webrtc::RtpExtension>& extensions) {
// Absolute Send Time extension id is used only with external auth,
// so do not bother searching for it and making asyncronious call to set
// something that is not used.
#if defined(ENABLE_EXTERNAL_AUTH)
const webrtc::RtpExtension* send_time_extension =
webrtc::RtpExtension::FindHeaderExtensionByUri(
extensions, webrtc::RtpExtension::kAbsSendTimeUri);
int rtp_abs_sendtime_extn_id =
send_time_extension ? send_time_extension->id : -1;
invoker_.AsyncInvoke<void>(
RTC_FROM_HERE, network_thread_,
Bind(&BaseChannel::CacheRtpAbsSendTimeHeaderExtension_n, this,
rtp_abs_sendtime_extn_id));
#endif
}
void BaseChannel::CacheRtpAbsSendTimeHeaderExtension_n(
int rtp_abs_sendtime_extn_id) {
if (sdes_transport_) {
sdes_transport_->CacheRtpAbsSendTimeHeaderExtension(
rtp_abs_sendtime_extn_id);
} else if (dtls_srtp_transport_) {
dtls_srtp_transport_->CacheRtpAbsSendTimeHeaderExtension(
rtp_abs_sendtime_extn_id);
} else {
RTC_LOG(LS_WARNING)
<< "Trying to cache the Absolute Send Time extension id "
"but the SRTP is not active.";
}
}
void BaseChannel::OnMessage(rtc::Message *pmsg) {
TRACE_EVENT0("webrtc", "BaseChannel::OnMessage");
switch (pmsg->message_id) {
@ -1077,7 +656,7 @@ void BaseChannel::OnMessage(rtc::Message *pmsg) {
}
void BaseChannel::AddHandledPayloadType(int payload_type) {
rtp_transport_->AddHandledPayloadType(payload_type);
bundle_filter_.AddPayloadType(payload_type);
}
void BaseChannel::FlushRtcpMessages_n() {
@ -1104,47 +683,6 @@ void BaseChannel::SignalSentPacket_w(const rtc::SentPacket& sent_packet) {
SignalSentPacket(sent_packet);
}
void BaseChannel::UpdateEncryptedHeaderExtensionIds(
cricket::ContentSource source,
const std::vector<int>& extension_ids) {
if (source == ContentSource::CS_LOCAL) {
cached_recv_extension_ids_ = std::move(extension_ids);
if (dtls_srtp_transport_) {
dtls_srtp_transport_->UpdateRecvEncryptedHeaderExtensionIds(
extension_ids);
}
} else {
cached_send_extension_ids_ = std::move(extension_ids);
if (dtls_srtp_transport_) {
dtls_srtp_transport_->UpdateSendEncryptedHeaderExtensionIds(
extension_ids);
}
}
}
void BaseChannel::ActivateRtcpMux() {
// We permanently activated RTCP muxing; signal that we no longer need
// the RTCP transport.
std::string debug_name =
transport_name_.empty()
? rtp_transport_->rtp_packet_transport()->transport_name()
: transport_name_;
RTC_LOG(LS_INFO) << "Enabling rtcp-mux for " << content_name()
<< "; no longer need RTCP transport for " << debug_name;
if (rtp_transport_->rtcp_packet_transport()) {
SetTransport_n(/*rtcp=*/true, nullptr, nullptr);
if (dtls_srtp_transport_) {
RTC_DCHECK(rtp_dtls_transport_);
dtls_srtp_transport_->SetDtlsTransports(rtp_dtls_transport_,
/*rtcp_dtls_transport_=*/nullptr);
} else {
rtp_transport_->SetRtcpPacketTransport(nullptr);
}
SignalRtcpMuxFullyActive(transport_name_);
}
UpdateWritableState_n();
}
VoiceChannel::VoiceChannel(rtc::Thread* worker_thread,
rtc::Thread* network_thread,
rtc::Thread* signaling_thread,
@ -1152,21 +690,20 @@ VoiceChannel::VoiceChannel(rtc::Thread* worker_thread,
MediaEngineInterface* /* media_engine */,
std::unique_ptr<VoiceMediaChannel> media_channel,
const std::string& content_name,
bool rtcp_mux_required,
bool srtp_required)
bool srtp_required,
rtc::CryptoOptions crypto_options)
: BaseChannel(worker_thread,
network_thread,
signaling_thread,
std::move(media_channel),
content_name,
rtcp_mux_required,
srtp_required) {}
srtp_required,
crypto_options) {}
VoiceChannel::~VoiceChannel() {
TRACE_EVENT0("webrtc", "VoiceChannel::~VoiceChannel");
// this can't be done in the base class, since it calls a virtual
DisableMedia_w();
Deinit();
}
void BaseChannel::UpdateMediaSendRecvState() {
@ -1208,11 +745,6 @@ bool VoiceChannel::SetLocalContent_w(const MediaContentDescription* content,
RtpHeaderExtensions rtp_header_extensions =
GetFilteredRtpHeaderExtensions(audio->rtp_header_extensions());
if (!SetRtpTransportParameters(content, type, CS_LOCAL, rtp_header_extensions,
error_desc)) {
return false;
}
AudioRecvParameters recv_params = last_recv_params_;
RtpParametersFromMediaDescription(audio, rtp_header_extensions, &recv_params);
if (!media_channel()->SetRecvParameters(recv_params)) {
@ -1257,11 +789,6 @@ bool VoiceChannel::SetRemoteContent_w(const MediaContentDescription* content,
RtpHeaderExtensions rtp_header_extensions =
GetFilteredRtpHeaderExtensions(audio->rtp_header_extensions());
if (!SetRtpTransportParameters(content, type, CS_REMOTE,
rtp_header_extensions, error_desc)) {
return false;
}
AudioSendParameters send_params = last_send_params_;
RtpSendParametersFromMediaDescription(audio, rtp_header_extensions,
&send_params);
@ -1284,10 +811,6 @@ bool VoiceChannel::SetRemoteContent_w(const MediaContentDescription* content,
return false;
}
if (audio->rtp_header_extensions_set()) {
MaybeCacheRtpAbsSendTimeHeaderExtension_w(rtp_header_extensions);
}
set_remote_content_direction(content->direction());
UpdateMediaSendRecvState_w();
return true;
@ -1298,22 +821,20 @@ VideoChannel::VideoChannel(rtc::Thread* worker_thread,
rtc::Thread* signaling_thread,
std::unique_ptr<VideoMediaChannel> media_channel,
const std::string& content_name,
bool rtcp_mux_required,
bool srtp_required)
bool srtp_required,
rtc::CryptoOptions crypto_options)
: BaseChannel(worker_thread,
network_thread,
signaling_thread,
std::move(media_channel),
content_name,
rtcp_mux_required,
srtp_required) {}
srtp_required,
crypto_options) {}
VideoChannel::~VideoChannel() {
TRACE_EVENT0("webrtc", "VideoChannel::~VideoChannel");
// this can't be done in the base class, since it calls a virtual
DisableMedia_w();
Deinit();
}
void VideoChannel::UpdateMediaSendRecvState_w() {
@ -1351,11 +872,6 @@ bool VideoChannel::SetLocalContent_w(const MediaContentDescription* content,
RtpHeaderExtensions rtp_header_extensions =
GetFilteredRtpHeaderExtensions(video->rtp_header_extensions());
if (!SetRtpTransportParameters(content, type, CS_LOCAL, rtp_header_extensions,
error_desc)) {
return false;
}
VideoRecvParameters recv_params = last_recv_params_;
RtpParametersFromMediaDescription(video, rtp_header_extensions, &recv_params);
if (!media_channel()->SetRecvParameters(recv_params)) {
@ -1400,11 +916,6 @@ bool VideoChannel::SetRemoteContent_w(const MediaContentDescription* content,
RtpHeaderExtensions rtp_header_extensions =
GetFilteredRtpHeaderExtensions(video->rtp_header_extensions());
if (!SetRtpTransportParameters(content, type, CS_REMOTE,
rtp_header_extensions, error_desc)) {
return false;
}
VideoSendParameters send_params = last_send_params_;
RtpSendParametersFromMediaDescription(video, rtp_header_extensions,
&send_params);
@ -1430,11 +941,6 @@ bool VideoChannel::SetRemoteContent_w(const MediaContentDescription* content,
SafeSetError("Failed to set remote video description streams.", error_desc);
return false;
}
if (video->rtp_header_extensions_set()) {
MaybeCacheRtpAbsSendTimeHeaderExtension_w(rtp_header_extensions);
}
set_remote_content_direction(content->direction());
UpdateMediaSendRecvState_w();
return true;
@ -1445,36 +951,20 @@ RtpDataChannel::RtpDataChannel(rtc::Thread* worker_thread,
rtc::Thread* signaling_thread,
std::unique_ptr<DataMediaChannel> media_channel,
const std::string& content_name,
bool rtcp_mux_required,
bool srtp_required)
bool srtp_required,
rtc::CryptoOptions crypto_options)
: BaseChannel(worker_thread,
network_thread,
signaling_thread,
std::move(media_channel),
content_name,
rtcp_mux_required,
srtp_required) {}
srtp_required,
crypto_options) {}
RtpDataChannel::~RtpDataChannel() {
TRACE_EVENT0("webrtc", "RtpDataChannel::~RtpDataChannel");
// this can't be done in the base class, since it calls a virtual
DisableMedia_w();
Deinit();
}
void RtpDataChannel::Init_w(
DtlsTransportInternal* rtp_dtls_transport,
DtlsTransportInternal* rtcp_dtls_transport,
rtc::PacketTransportInternal* rtp_packet_transport,
rtc::PacketTransportInternal* rtcp_packet_transport) {
BaseChannel::Init_w(rtp_dtls_transport, rtcp_dtls_transport,
rtp_packet_transport, rtcp_packet_transport);
media_channel()->SignalDataReceived.connect(this,
&RtpDataChannel::OnDataReceived);
media_channel()->SignalReadyToSend.connect(
this, &RtpDataChannel::OnDataChannelReadyToSend);
}
void RtpDataChannel::Init_w(webrtc::RtpTransportInternal* rtp_transport) {
@ -1529,11 +1019,6 @@ bool RtpDataChannel::SetLocalContent_w(const MediaContentDescription* content,
RtpHeaderExtensions rtp_header_extensions =
GetFilteredRtpHeaderExtensions(data->rtp_header_extensions());
if (!SetRtpTransportParameters(content, type, CS_LOCAL, rtp_header_extensions,
error_desc)) {
return false;
}
DataRecvParameters recv_params = last_recv_params_;
RtpParametersFromMediaDescription(data, rtp_header_extensions, &recv_params);
if (!media_channel()->SetRecvParameters(recv_params)) {
@ -1588,11 +1073,6 @@ bool RtpDataChannel::SetRemoteContent_w(const MediaContentDescription* content,
GetFilteredRtpHeaderExtensions(data->rtp_header_extensions());
RTC_LOG(LS_INFO) << "Setting remote data description";
if (!SetRtpTransportParameters(content, type, CS_REMOTE,
rtp_header_extensions, error_desc)) {
return false;
}
DataSendParameters send_params = last_send_params_;
RtpSendParametersFromMediaDescription<DataCodec>(data, rtp_header_extensions,
&send_params);

View File

@ -29,13 +29,12 @@
#include "p2p/base/dtlstransportinternal.h"
#include "p2p/base/packettransportinternal.h"
#include "pc/audiomonitor.h"
#include "pc/bundlefilter.h"
#include "pc/dtlssrtptransport.h"
#include "pc/mediasession.h"
#include "pc/rtcpmuxfilter.h"
#include "pc/rtptransport.h"
#include "pc/srtpfilter.h"
#include "pc/srtptransport.h"
#include "pc/transportcontroller.h"
#include "rtc_base/asyncinvoker.h"
#include "rtc_base/asyncudpsocket.h"
#include "rtc_base/criticalsection.h"
@ -75,20 +74,16 @@ class BaseChannel
public:
// If |srtp_required| is true, the channel will not send or receive any
// RTP/RTCP packets without using SRTP (either using SDES or DTLS-SRTP).
// TODO(zhihuang:) Create a BaseChannel::Config struct for the parameter lists
// which will make it easier to change the constructor.
BaseChannel(rtc::Thread* worker_thread,
rtc::Thread* network_thread,
rtc::Thread* signaling_thread,
std::unique_ptr<MediaChannel> media_channel,
const std::string& content_name,
bool rtcp_mux_required,
bool srtp_required);
bool srtp_required,
rtc::CryptoOptions crypto_options);
virtual ~BaseChannel();
// TODO(zhihuang): Remove this once the RtpTransport can be shared between
// BaseChannels.
void Init_w(DtlsTransportInternal* rtp_dtls_transport,
DtlsTransportInternal* rtcp_dtls_transport,
rtc::PacketTransportInternal* rtp_packet_transport,
rtc::PacketTransportInternal* rtcp_packet_transport);
void Init_w(webrtc::RtpTransportInternal* rtp_transport);
// Deinit may be called multiple times and is simply ignored if it's already
@ -102,16 +97,10 @@ class BaseChannel
const std::string& transport_name() const { return transport_name_; }
bool enabled() const { return enabled_; }
// This function returns true if we are using SDES.
bool sdes_active() const {
return sdes_transport_ && sdes_negotiator_.IsActive();
}
// The following function returns true if we are using DTLS-based keying.
bool dtls_active() const {
return dtls_srtp_transport_ && dtls_srtp_transport_->IsActive();
}
// This function returns true if using SRTP (DTLS-based keying or SDES).
bool srtp_active() const { return sdes_active() || dtls_active(); }
bool srtp_active() const {
return rtp_transport_ && rtp_transport_->IsSrtpActive();
}
bool writable() const { return writable_; }
@ -121,20 +110,6 @@ class BaseChannel
// internally. It would replace the |SetTransports| and its variants.
void SetRtpTransport(webrtc::RtpTransportInternal* rtp_transport);
// Set the transport(s), and update writability and "ready-to-send" state.
// |rtp_transport| must be non-null.
// |rtcp_transport| must be supplied if NeedsRtcpTransport() is true (meaning
// RTCP muxing is not fully active yet).
// |rtp_transport| and |rtcp_transport| must share the same transport name as
// well.
// Can not start with "rtc::PacketTransportInternal" and switch to
// "DtlsTransportInternal", or vice-versa.
// TODO(zhihuang): Remove these two once the RtpTransport can be shared
// between BaseChannels.
void SetTransports(DtlsTransportInternal* rtp_dtls_transport,
DtlsTransportInternal* rtcp_dtls_transport);
void SetTransports(rtc::PacketTransportInternal* rtp_packet_transport,
rtc::PacketTransportInternal* rtcp_packet_transport);
// Channel control
bool SetLocalContent(const MediaContentDescription* content,
webrtc::SdpType type,
@ -173,15 +148,19 @@ class BaseChannel
// Fired on the network thread.
sigslot::signal1<const std::string&> SignalRtcpMuxFullyActive;
// Only public for unit tests. Otherwise, consider private.
DtlsTransportInternal* rtp_dtls_transport() const {
return rtp_dtls_transport_;
}
DtlsTransportInternal* rtcp_dtls_transport() const {
return rtcp_dtls_transport_;
rtc::PacketTransportInternal* rtp_packet_transport() {
if (rtp_transport_) {
return rtp_transport_->rtp_packet_transport();
}
return nullptr;
}
bool NeedsRtcpTransport();
rtc::PacketTransportInternal* rtcp_packet_transport() {
if (rtp_transport_) {
return rtp_transport_->rtcp_packet_transport();
}
return nullptr;
}
// From RtpTransport - public for testing only
void OnTransportReadyToSend(bool ready);
@ -207,22 +186,11 @@ class BaseChannel
void SetMetricsObserver(
rtc::scoped_refptr<webrtc::MetricsObserverInterface> metrics_observer);
void DisableEncryption(bool disabled) { encryption_disabled_ = disabled; }
protected:
virtual MediaChannel* media_channel() const { return media_channel_.get(); }
void SetTransports_n(DtlsTransportInternal* rtp_dtls_transport,
DtlsTransportInternal* rtcp_dtls_transport,
rtc::PacketTransportInternal* rtp_packet_transport,
rtc::PacketTransportInternal* rtcp_packet_transport);
// This does not update writability or "ready-to-send" state; it just
// disconnects from the old channel and connects to the new one.
// TODO(zhihuang): Remove this once the RtpTransport can be shared between
// BaseChannels.
void SetTransport_n(bool rtcp,
DtlsTransportInternal* new_dtls_transport,
rtc::PacketTransportInternal* new_packet_transport);
bool was_ever_writable() const { return was_ever_writable_; }
void set_local_content_direction(webrtc::RtpTransceiverDirection direction) {
local_content_direction_ = direction;
@ -289,11 +257,6 @@ class BaseChannel
bool RemoveRecvStream_w(uint32_t ssrc);
bool AddSendStream_w(const StreamParams& sp);
bool RemoveSendStream_w(uint32_t ssrc);
bool ShouldSetupDtlsSrtp_n() const;
// Do the DTLS key expansion and impose it on the SRTP/SRTCP filters.
// |rtcp_channel| indicates whether to set up the RTP or RTCP filter.
bool SetupDtlsSrtp_n(bool rtcp);
void MaybeSetupDtlsSrtp_n();
// Should be called whenever the conditions for
// IsReadyToReceiveMedia/IsReadyToSendMedia are satisfied (or unsatisfied).
@ -313,18 +276,6 @@ class BaseChannel
virtual bool SetRemoteContent_w(const MediaContentDescription* content,
webrtc::SdpType type,
std::string* error_desc) = 0;
bool SetRtpTransportParameters(const MediaContentDescription* content,
webrtc::SdpType type,
ContentSource src,
const RtpHeaderExtensions& extensions,
std::string* error_desc);
bool SetRtpTransportParameters_n(
const MediaContentDescription* content,
webrtc::SdpType type,
ContentSource src,
const std::vector<int>& encrypted_extension_ids,
std::string* error_desc);
// Return a list of RTP header extensions with the non-encrypted extensions
// removed depending on the current crypto_options_ and only if both the
// non-encrypted and encrypted extension is present for the same URI.
@ -336,19 +287,6 @@ class BaseChannel
void MaybeCacheRtpAbsSendTimeHeaderExtension_w(
const std::vector<webrtc::RtpExtension>& extensions);
bool CheckSrtpConfig_n(const std::vector<CryptoParams>& cryptos,
bool* dtls,
std::string* error_desc);
bool SetSrtp_n(const std::vector<CryptoParams>& params,
webrtc::SdpType type,
ContentSource src,
const std::vector<int>& encrypted_extension_ids,
std::string* error_desc);
bool SetRtcpMux_n(bool enable,
webrtc::SdpType type,
ContentSource src,
std::string* error_desc);
// From MessageHandler
void OnMessage(rtc::Message* pmsg) override;
@ -366,26 +304,6 @@ class BaseChannel
void SignalSentPacket_n(const rtc::SentPacket& sent_packet);
void SignalSentPacket_w(const rtc::SentPacket& sent_packet);
bool IsReadyToSendMedia_n() const;
void CacheRtpAbsSendTimeHeaderExtension_n(int rtp_abs_sendtime_extn_id);
// Wraps the existing RtpTransport in an SrtpTransport.
void EnableSdes_n();
// Wraps the existing RtpTransport in a new SrtpTransport and wraps that in a
// new DtlsSrtpTransport.
void EnableDtlsSrtp_n();
// Update the encrypted header extension IDs when setting the local/remote
// description and use them later together with other crypto parameters from
// DtlsTransport. If DTLS-SRTP is enabled, it also update the encrypted header
// extension IDs for DtlsSrtpTransport.
void UpdateEncryptedHeaderExtensionIds(cricket::ContentSource source,
const std::vector<int>& extension_ids);
// Permanently enable RTCP muxing. Set null RTCP PacketTransport for
// BaseChannel and RtpTransport. If using DTLS-SRTP, set null DtlsTransport
// for DtlsSrtpTransport.
void ActivateRtcpMux();
rtc::Thread* const worker_thread_;
rtc::Thread* const network_thread_;
rtc::Thread* const signaling_thread_;
@ -396,16 +314,8 @@ class BaseChannel
// Won't be set when using raw packet transports. SDP-specific thing.
std::string transport_name_;
const bool rtcp_mux_required_;
rtc::scoped_refptr<webrtc::MetricsObserverInterface> metrics_observer_;
// Separate DTLS/non-DTLS pointers to support using BaseChannel without DTLS.
// Temporary measure until more refactoring is done.
// If non-null, "X_dtls_transport_" will always equal "X_packet_transport_".
DtlsTransportInternal* rtp_dtls_transport_ = nullptr;
DtlsTransportInternal* rtcp_dtls_transport_ = nullptr;
webrtc::RtpTransportInternal* rtp_transport_ = nullptr;
// Only one of these transports is non-null at a time. One for DTLS-SRTP, one
// for SDES and one for unencrypted RTP.
@ -415,12 +325,11 @@ class BaseChannel
std::vector<std::pair<rtc::Socket::Option, int> > socket_options_;
std::vector<std::pair<rtc::Socket::Option, int> > rtcp_socket_options_;
SrtpFilter sdes_negotiator_;
RtcpMuxFilter rtcp_mux_filter_;
bool writable_ = false;
bool was_ever_writable_ = false;
bool has_received_packet_ = false;
const bool srtp_required_ = true;
rtc::CryptoOptions crypto_options_;
// MediaChannel related members that should be accessed from the worker
// thread.
@ -439,6 +348,11 @@ class BaseChannel
// The cached encrypted header extension IDs.
rtc::Optional<std::vector<int>> cached_send_extension_ids_;
rtc::Optional<std::vector<int>> cached_recv_extension_ids_;
// TODO(zhihuang): These two variables can be removed once switching to
// RtpDemuxer.
BundleFilter bundle_filter_;
bool encryption_disabled_ = false;
};
// VoiceChannel is a specialization that adds support for early media, DTMF,
@ -451,8 +365,8 @@ class VoiceChannel : public BaseChannel {
MediaEngineInterface* media_engine,
std::unique_ptr<VoiceMediaChannel> channel,
const std::string& content_name,
bool rtcp_mux_required,
bool srtp_required);
bool srtp_required,
rtc::CryptoOptions crypto_options);
~VoiceChannel();
// downcasts a MediaChannel
@ -491,8 +405,8 @@ class VideoChannel : public BaseChannel {
rtc::Thread* signaling_thread,
std::unique_ptr<VideoMediaChannel> media_channel,
const std::string& content_name,
bool rtcp_mux_required,
bool srtp_required);
bool srtp_required,
rtc::CryptoOptions crypto_options);
~VideoChannel();
// downcasts a MediaChannel
@ -530,8 +444,8 @@ class RtpDataChannel : public BaseChannel {
rtc::Thread* signaling_thread,
std::unique_ptr<DataMediaChannel> channel,
const std::string& content_name,
bool rtcp_mux_required,
bool srtp_required);
bool srtp_required,
rtc::CryptoOptions crypto_options);
~RtpDataChannel();
// TODO(zhihuang): Remove this once the RtpTransport can be shared between
// BaseChannels.

File diff suppressed because it is too large Load Diff

View File

@ -152,38 +152,6 @@ void ChannelManager::Terminate() {
initialized_ = false;
}
VoiceChannel* ChannelManager::CreateVoiceChannel(
webrtc::Call* call,
const cricket::MediaConfig& media_config,
DtlsTransportInternal* rtp_transport,
DtlsTransportInternal* rtcp_transport,
rtc::Thread* signaling_thread,
const std::string& content_name,
bool srtp_required,
const AudioOptions& options) {
return worker_thread_->Invoke<VoiceChannel*>(RTC_FROM_HERE, [&] {
return CreateVoiceChannel_w(
call, media_config, rtp_transport, rtcp_transport, rtp_transport,
rtcp_transport, signaling_thread, content_name, srtp_required, options);
});
}
VoiceChannel* ChannelManager::CreateVoiceChannel(
webrtc::Call* call,
const cricket::MediaConfig& media_config,
rtc::PacketTransportInternal* rtp_transport,
rtc::PacketTransportInternal* rtcp_transport,
rtc::Thread* signaling_thread,
const std::string& content_name,
bool srtp_required,
const AudioOptions& options) {
return worker_thread_->Invoke<VoiceChannel*>(RTC_FROM_HERE, [&] {
return CreateVoiceChannel_w(call, media_config, nullptr, nullptr,
rtp_transport, rtcp_transport, signaling_thread,
content_name, srtp_required, options);
});
}
VoiceChannel* ChannelManager::CreateVoiceChannel(
webrtc::Call* call,
const cricket::MediaConfig& media_config,
@ -191,12 +159,13 @@ VoiceChannel* ChannelManager::CreateVoiceChannel(
rtc::Thread* signaling_thread,
const std::string& content_name,
bool srtp_required,
const rtc::CryptoOptions& crypto_options,
const AudioOptions& options) {
if (!worker_thread_->IsCurrent()) {
return worker_thread_->Invoke<VoiceChannel*>(RTC_FROM_HERE, [&] {
return CreateVoiceChannel(call, media_config, rtp_transport,
signaling_thread, content_name, srtp_required,
options);
crypto_options, options);
});
}
@ -215,8 +184,8 @@ VoiceChannel* ChannelManager::CreateVoiceChannel(
auto voice_channel = rtc::MakeUnique<VoiceChannel>(
worker_thread_, network_thread_, signaling_thread, media_engine_.get(),
rtc::WrapUnique(media_channel), content_name,
rtp_transport->rtcp_packet_transport() == nullptr, srtp_required);
rtc::WrapUnique(media_channel), content_name, srtp_required,
crypto_options);
voice_channel->Init_w(rtp_transport);
@ -225,41 +194,6 @@ VoiceChannel* ChannelManager::CreateVoiceChannel(
return voice_channel_ptr;
}
VoiceChannel* ChannelManager::CreateVoiceChannel_w(
webrtc::Call* call,
const cricket::MediaConfig& media_config,
DtlsTransportInternal* rtp_dtls_transport,
DtlsTransportInternal* rtcp_dtls_transport,
rtc::PacketTransportInternal* rtp_packet_transport,
rtc::PacketTransportInternal* rtcp_packet_transport,
rtc::Thread* signaling_thread,
const std::string& content_name,
bool srtp_required,
const AudioOptions& options) {
RTC_DCHECK_RUN_ON(worker_thread_);
RTC_DCHECK(initialized_);
RTC_DCHECK(call);
if (!media_engine_) {
return nullptr;
}
VoiceMediaChannel* media_channel = media_engine_->CreateChannel(
call, media_config, options);
if (!media_channel) {
return nullptr;
}
auto voice_channel = rtc::MakeUnique<VoiceChannel>(
worker_thread_, network_thread_, signaling_thread, media_engine_.get(),
rtc::WrapUnique(media_channel), content_name,
rtcp_packet_transport == nullptr, srtp_required);
voice_channel->Init_w(rtp_dtls_transport, rtcp_dtls_transport,
rtp_packet_transport, rtcp_packet_transport);
VoiceChannel* voice_channel_ptr = voice_channel.get();
voice_channels_.push_back(std::move(voice_channel));
return voice_channel_ptr;
}
void ChannelManager::DestroyVoiceChannel(VoiceChannel* voice_channel) {
TRACE_EVENT0("webrtc", "ChannelManager::DestroyVoiceChannel");
@ -286,38 +220,6 @@ void ChannelManager::DestroyVoiceChannel(VoiceChannel* voice_channel) {
voice_channels_.erase(it);
}
VideoChannel* ChannelManager::CreateVideoChannel(
webrtc::Call* call,
const cricket::MediaConfig& media_config,
DtlsTransportInternal* rtp_transport,
DtlsTransportInternal* rtcp_transport,
rtc::Thread* signaling_thread,
const std::string& content_name,
bool srtp_required,
const VideoOptions& options) {
return worker_thread_->Invoke<VideoChannel*>(RTC_FROM_HERE, [&] {
return CreateVideoChannel_w(
call, media_config, rtp_transport, rtcp_transport, rtp_transport,
rtcp_transport, signaling_thread, content_name, srtp_required, options);
});
}
VideoChannel* ChannelManager::CreateVideoChannel(
webrtc::Call* call,
const cricket::MediaConfig& media_config,
rtc::PacketTransportInternal* rtp_transport,
rtc::PacketTransportInternal* rtcp_transport,
rtc::Thread* signaling_thread,
const std::string& content_name,
bool srtp_required,
const VideoOptions& options) {
return worker_thread_->Invoke<VideoChannel*>(RTC_FROM_HERE, [&] {
return CreateVideoChannel_w(call, media_config, nullptr, nullptr,
rtp_transport, rtcp_transport, signaling_thread,
content_name, srtp_required, options);
});
}
VideoChannel* ChannelManager::CreateVideoChannel(
webrtc::Call* call,
const cricket::MediaConfig& media_config,
@ -325,12 +227,13 @@ VideoChannel* ChannelManager::CreateVideoChannel(
rtc::Thread* signaling_thread,
const std::string& content_name,
bool srtp_required,
const rtc::CryptoOptions& crypto_options,
const VideoOptions& options) {
if (!worker_thread_->IsCurrent()) {
return worker_thread_->Invoke<VideoChannel*>(RTC_FROM_HERE, [&] {
return CreateVideoChannel(call, media_config, rtp_transport,
signaling_thread, content_name, srtp_required,
options);
crypto_options, options);
});
}
@ -349,8 +252,8 @@ VideoChannel* ChannelManager::CreateVideoChannel(
auto video_channel = rtc::MakeUnique<VideoChannel>(
worker_thread_, network_thread_, signaling_thread,
rtc::WrapUnique(media_channel), content_name,
rtp_transport->rtcp_packet_transport() == nullptr, srtp_required);
rtc::WrapUnique(media_channel), content_name, srtp_required,
crypto_options);
video_channel->Init_w(rtp_transport);
VideoChannel* video_channel_ptr = video_channel.get();
@ -358,41 +261,7 @@ VideoChannel* ChannelManager::CreateVideoChannel(
return video_channel_ptr;
}
VideoChannel* ChannelManager::CreateVideoChannel_w(
webrtc::Call* call,
const cricket::MediaConfig& media_config,
DtlsTransportInternal* rtp_dtls_transport,
DtlsTransportInternal* rtcp_dtls_transport,
rtc::PacketTransportInternal* rtp_packet_transport,
rtc::PacketTransportInternal* rtcp_packet_transport,
rtc::Thread* signaling_thread,
const std::string& content_name,
bool srtp_required,
const VideoOptions& options) {
RTC_DCHECK_RUN_ON(worker_thread_);
RTC_DCHECK(initialized_);
RTC_DCHECK(call);
if (!media_engine_) {
return nullptr;
}
VideoMediaChannel* media_channel = media_engine_->CreateVideoChannel(
call, media_config, options);
if (!media_channel) {
return nullptr;
}
auto video_channel = rtc::MakeUnique<VideoChannel>(
worker_thread_, network_thread_, signaling_thread,
rtc::WrapUnique(media_channel), content_name,
rtcp_packet_transport == nullptr, srtp_required);
video_channel->Init_w(rtp_dtls_transport, rtcp_dtls_transport,
rtp_packet_transport, rtcp_packet_transport);
VideoChannel* video_channel_ptr = video_channel.get();
video_channels_.push_back(std::move(video_channel));
return video_channel_ptr;
}
void ChannelManager::DestroyVideoChannel(VideoChannel* video_channel) {
TRACE_EVENT0("webrtc", "ChannelManager::DestroyVideoChannel");
@ -419,51 +288,17 @@ void ChannelManager::DestroyVideoChannel(VideoChannel* video_channel) {
video_channels_.erase(it);
}
RtpDataChannel* ChannelManager::CreateRtpDataChannel(
const cricket::MediaConfig& media_config,
DtlsTransportInternal* rtp_transport,
DtlsTransportInternal* rtcp_transport,
rtc::Thread* signaling_thread,
const std::string& content_name,
bool srtp_required) {
if (!worker_thread_->IsCurrent()) {
return worker_thread_->Invoke<RtpDataChannel*>(RTC_FROM_HERE, [&] {
return CreateRtpDataChannel(media_config, rtp_transport, rtcp_transport,
signaling_thread, content_name,
srtp_required);
});
}
// This is ok to alloc from a thread other than the worker thread.
RTC_DCHECK(initialized_);
DataMediaChannel* media_channel = data_engine_->CreateChannel(media_config);
if (!media_channel) {
RTC_LOG(LS_WARNING) << "Failed to create RTP data channel.";
return nullptr;
}
auto data_channel = rtc::MakeUnique<RtpDataChannel>(
worker_thread_, network_thread_, signaling_thread,
rtc::WrapUnique(media_channel), content_name, rtcp_transport == nullptr,
srtp_required);
data_channel->Init_w(rtp_transport, rtcp_transport, rtp_transport,
rtcp_transport);
RtpDataChannel* data_channel_ptr = data_channel.get();
data_channels_.push_back(std::move(data_channel));
return data_channel_ptr;
}
RtpDataChannel* ChannelManager::CreateRtpDataChannel(
const cricket::MediaConfig& media_config,
webrtc::RtpTransportInternal* rtp_transport,
rtc::Thread* signaling_thread,
const std::string& content_name,
bool srtp_required) {
bool srtp_required,
const rtc::CryptoOptions& crypto_options) {
if (!worker_thread_->IsCurrent()) {
return worker_thread_->Invoke<RtpDataChannel*>(RTC_FROM_HERE, [&] {
return CreateRtpDataChannel(media_config, rtp_transport, signaling_thread,
content_name, srtp_required);
content_name, srtp_required, crypto_options);
});
}
@ -477,8 +312,8 @@ RtpDataChannel* ChannelManager::CreateRtpDataChannel(
auto data_channel = rtc::MakeUnique<RtpDataChannel>(
worker_thread_, network_thread_, signaling_thread,
rtc::WrapUnique(media_channel), content_name,
rtp_transport->rtcp_packet_transport() == nullptr, srtp_required);
rtc::WrapUnique(media_channel), content_name, srtp_required,
crypto_options);
data_channel->Init_w(rtp_transport);
RtpDataChannel* data_channel_ptr = data_channel.get();

View File

@ -80,90 +80,38 @@ class ChannelManager final {
// call the appropriate Destroy*Channel method when done.
// Creates a voice channel, to be associated with the specified session.
// TODO(zhihuang): Replace this with the method taking an
// RtpTransportInternal*;
VoiceChannel* CreateVoiceChannel(
webrtc::Call* call,
const cricket::MediaConfig& media_config,
DtlsTransportInternal* rtp_transport,
DtlsTransportInternal* rtcp_transport,
rtc::Thread* signaling_thread,
const std::string& content_name,
bool srtp_required,
const AudioOptions& options);
// Version of the above that takes PacketTransportInternal.
// TODO(zhihuang): Replace this with the method taking an
// RtpTransportInternal*;
VoiceChannel* CreateVoiceChannel(
webrtc::Call* call,
const cricket::MediaConfig& media_config,
rtc::PacketTransportInternal* rtp_transport,
rtc::PacketTransportInternal* rtcp_transport,
rtc::Thread* signaling_thread,
const std::string& content_name,
bool srtp_required,
const AudioOptions& options);
VoiceChannel* CreateVoiceChannel(webrtc::Call* call,
const cricket::MediaConfig& media_config,
webrtc::RtpTransportInternal* rtp_transport,
rtc::Thread* signaling_thread,
const std::string& content_name,
bool srtp_required,
const rtc::CryptoOptions& crypto_options,
const AudioOptions& options);
// Destroys a voice channel created by CreateVoiceChannel.
void DestroyVoiceChannel(VoiceChannel* voice_channel);
// Creates a video channel, synced with the specified voice channel, and
// associated with the specified session.
// TODO(zhihuang): Replace this with the method taking an
// RtpTransportInternal*;
VideoChannel* CreateVideoChannel(
webrtc::Call* call,
const cricket::MediaConfig& media_config,
DtlsTransportInternal* rtp_transport,
DtlsTransportInternal* rtcp_transport,
rtc::Thread* signaling_thread,
const std::string& content_name,
bool srtp_required,
const VideoOptions& options);
// Version of the above that takes PacketTransportInternal.
// TODO(zhihuang): Replace this with the method taking an
// RtpTransportInternal*;
VideoChannel* CreateVideoChannel(
webrtc::Call* call,
const cricket::MediaConfig& media_config,
rtc::PacketTransportInternal* rtp_transport,
rtc::PacketTransportInternal* rtcp_transport,
rtc::Thread* signaling_thread,
const std::string& content_name,
bool srtp_required,
const VideoOptions& options);
VideoChannel* CreateVideoChannel(webrtc::Call* call,
const cricket::MediaConfig& media_config,
webrtc::RtpTransportInternal* rtp_transport,
rtc::Thread* signaling_thread,
const std::string& content_name,
bool srtp_required,
const rtc::CryptoOptions& crypto_options,
const VideoOptions& options);
// Destroys a video channel created by CreateVideoChannel.
void DestroyVideoChannel(VideoChannel* video_channel);
// TODO(zhihuang): Replace this with the method taking an
// RtpTransportInternal*;
RtpDataChannel* CreateRtpDataChannel(
const cricket::MediaConfig& media_config,
DtlsTransportInternal* rtp_transport,
DtlsTransportInternal* rtcp_transport,
rtc::Thread* signaling_thread,
const std::string& content_name,
bool srtp_required);
RtpDataChannel* CreateRtpDataChannel(
const cricket::MediaConfig& media_config,
webrtc::RtpTransportInternal* rtp_transport,
rtc::Thread* signaling_thread,
const std::string& content_name,
bool srtp_required);
bool srtp_required,
const rtc::CryptoOptions& crypto_options);
// Destroys a data channel created by CreateRtpDataChannel.
void DestroyRtpDataChannel(RtpDataChannel* data_channel);
@ -191,29 +139,6 @@ class ChannelManager final {
void StopAecDump();
private:
VoiceChannel* CreateVoiceChannel_w(
webrtc::Call* call,
const cricket::MediaConfig& media_config,
DtlsTransportInternal* rtp_dtls_transport,
DtlsTransportInternal* rtcp_dtls_transport,
rtc::PacketTransportInternal* rtp_packet_transport,
rtc::PacketTransportInternal* rtcp_packet_transport,
rtc::Thread* signaling_thread,
const std::string& content_name,
bool srtp_required,
const AudioOptions& options);
VideoChannel* CreateVideoChannel_w(
webrtc::Call* call,
const cricket::MediaConfig& media_config,
DtlsTransportInternal* rtp_dtls_transport,
DtlsTransportInternal* rtcp_dtls_transport,
rtc::PacketTransportInternal* rtp_packet_transport,
rtc::PacketTransportInternal* rtcp_packet_transport,
rtc::Thread* signaling_thread,
const std::string& content_name,
bool srtp_required,
const VideoOptions& options);
std::unique_ptr<MediaEngineInterface> media_engine_; // Nullable.
std::unique_ptr<DataEngineInterface> data_engine_; // Non-null.
bool initialized_ = false;

View File

@ -15,8 +15,8 @@
#include "media/base/fakevideocapturer.h"
#include "media/base/testutils.h"
#include "media/engine/fakewebrtccall.h"
#include "p2p/base/fakedtlstransport.h"
#include "pc/channelmanager.h"
#include "pc/test/faketransportcontroller.h"
#include "rtc_base/gunit.h"
#include "rtc_base/logging.h"
#include "rtc_base/thread.h"
@ -47,13 +47,47 @@ class ChannelManagerTest : public testing::Test {
std::unique_ptr<DataEngineInterface>(fdme_),
rtc::Thread::Current(),
rtc::Thread::Current())),
fake_call_(),
transport_controller_(
new cricket::FakeTransportController(ICEROLE_CONTROLLING)) {
fake_call_() {
fme_->SetAudioCodecs(MAKE_VECTOR(kAudioCodecs));
fme_->SetVideoCodecs(MAKE_VECTOR(kVideoCodecs));
}
std::unique_ptr<webrtc::RtpTransportInternal> CreateDtlsSrtpTransport() {
rtp_dtls_transport_ = rtc::MakeUnique<FakeDtlsTransport>(
"fake_dtls_transport", cricket::ICE_CANDIDATE_COMPONENT_RTP);
auto rtp_transport =
rtc::MakeUnique<webrtc::RtpTransport>(/*rtcp_mux_required=*/true);
auto srtp_transport =
rtc::MakeUnique<webrtc::SrtpTransport>(std::move(rtp_transport));
auto dtls_srtp_transport =
rtc::MakeUnique<webrtc::DtlsSrtpTransport>(std::move(srtp_transport));
dtls_srtp_transport->SetDtlsTransports(rtp_dtls_transport_.get(),
/*rtcp_dtls_transport=*/nullptr);
return dtls_srtp_transport;
}
void TestCreateDestroyChannels(webrtc::RtpTransportInternal* rtp_transport) {
cricket::VoiceChannel* voice_channel = cm_->CreateVoiceChannel(
&fake_call_, cricket::MediaConfig(), rtp_transport,
rtc::Thread::Current(), cricket::CN_AUDIO, kDefaultSrtpRequired,
rtc::CryptoOptions(), AudioOptions());
EXPECT_TRUE(voice_channel != nullptr);
cricket::VideoChannel* video_channel = cm_->CreateVideoChannel(
&fake_call_, cricket::MediaConfig(), rtp_transport,
rtc::Thread::Current(), cricket::CN_VIDEO, kDefaultSrtpRequired,
rtc::CryptoOptions(), VideoOptions());
EXPECT_TRUE(video_channel != nullptr);
cricket::RtpDataChannel* rtp_data_channel = cm_->CreateRtpDataChannel(
cricket::MediaConfig(), rtp_transport, rtc::Thread::Current(),
cricket::CN_DATA, kDefaultSrtpRequired, rtc::CryptoOptions());
EXPECT_TRUE(rtp_data_channel != nullptr);
cm_->DestroyVideoChannel(video_channel);
cm_->DestroyVoiceChannel(voice_channel);
cm_->DestroyRtpDataChannel(rtp_data_channel);
cm_->Terminate();
}
std::unique_ptr<DtlsTransportInternal> rtp_dtls_transport_;
std::unique_ptr<rtc::Thread> network_;
std::unique_ptr<rtc::Thread> worker_;
// |fme_| and |fdme_| are actually owned by |cm_|.
@ -61,7 +95,6 @@ class ChannelManagerTest : public testing::Test {
cricket::FakeDataEngine* fdme_;
std::unique_ptr<cricket::ChannelManager> cm_;
cricket::FakeCall fake_call_;
std::unique_ptr<cricket::FakeTransportController> transport_controller_;
};
// Test that we startup/shutdown properly.
@ -93,68 +126,6 @@ TEST_F(ChannelManagerTest, StartupShutdownOnThread) {
EXPECT_FALSE(cm_->initialized());
}
// Test that we can create and destroy a voice and video channel.
TEST_F(ChannelManagerTest, CreateDestroyChannels) {
EXPECT_TRUE(cm_->Init());
cricket::DtlsTransportInternal* rtp_transport =
transport_controller_->CreateDtlsTransport(
cricket::CN_AUDIO, cricket::ICE_CANDIDATE_COMPONENT_RTP);
cricket::VoiceChannel* voice_channel = cm_->CreateVoiceChannel(
&fake_call_, cricket::MediaConfig(),
rtp_transport, nullptr /*rtcp_transport*/,
rtc::Thread::Current(), cricket::CN_AUDIO, kDefaultSrtpRequired,
AudioOptions());
EXPECT_TRUE(voice_channel != nullptr);
cricket::VideoChannel* video_channel = cm_->CreateVideoChannel(
&fake_call_, cricket::MediaConfig(),
rtp_transport, nullptr /*rtcp_transport*/,
rtc::Thread::Current(), cricket::CN_VIDEO, kDefaultSrtpRequired,
VideoOptions());
EXPECT_TRUE(video_channel != nullptr);
cricket::RtpDataChannel* rtp_data_channel = cm_->CreateRtpDataChannel(
cricket::MediaConfig(), rtp_transport, nullptr /*rtcp_transport*/,
rtc::Thread::Current(), cricket::CN_DATA, kDefaultSrtpRequired);
EXPECT_TRUE(rtp_data_channel != nullptr);
cm_->DestroyVideoChannel(video_channel);
cm_->DestroyVoiceChannel(voice_channel);
cm_->DestroyRtpDataChannel(rtp_data_channel);
cm_->Terminate();
}
// Test that we can create and destroy a voice and video channel with a worker.
TEST_F(ChannelManagerTest, CreateDestroyChannelsOnThread) {
network_->Start();
worker_->Start();
EXPECT_TRUE(cm_->set_worker_thread(worker_.get()));
EXPECT_TRUE(cm_->set_network_thread(network_.get()));
EXPECT_TRUE(cm_->Init());
transport_controller_.reset(new cricket::FakeTransportController(
network_.get(), ICEROLE_CONTROLLING));
cricket::DtlsTransportInternal* rtp_transport =
transport_controller_->CreateDtlsTransport(
cricket::CN_AUDIO, cricket::ICE_CANDIDATE_COMPONENT_RTP);
cricket::VoiceChannel* voice_channel = cm_->CreateVoiceChannel(
&fake_call_, cricket::MediaConfig(),
rtp_transport, nullptr /*rtcp_transport*/,
rtc::Thread::Current(), cricket::CN_AUDIO, kDefaultSrtpRequired,
AudioOptions());
EXPECT_TRUE(voice_channel != nullptr);
cricket::VideoChannel* video_channel = cm_->CreateVideoChannel(
&fake_call_, cricket::MediaConfig(),
rtp_transport, nullptr /*rtcp_transport*/,
rtc::Thread::Current(), cricket::CN_VIDEO, kDefaultSrtpRequired,
VideoOptions());
EXPECT_TRUE(video_channel != nullptr);
cricket::RtpDataChannel* rtp_data_channel = cm_->CreateRtpDataChannel(
cricket::MediaConfig(), rtp_transport, nullptr /*rtcp_transport*/,
rtc::Thread::Current(), cricket::CN_DATA, kDefaultSrtpRequired);
EXPECT_TRUE(rtp_data_channel != nullptr);
cm_->DestroyVideoChannel(video_channel);
cm_->DestroyVoiceChannel(voice_channel);
cm_->DestroyRtpDataChannel(rtp_data_channel);
cm_->Terminate();
}
TEST_F(ChannelManagerTest, SetVideoRtxEnabled) {
std::vector<VideoCodec> codecs;
const VideoCodec rtx_codec(96, "rtx");
@ -185,89 +156,20 @@ TEST_F(ChannelManagerTest, SetVideoRtxEnabled) {
EXPECT_TRUE(ContainsMatchingCodec(codecs, rtx_codec));
}
enum class RTPTransportType { kRtp, kSrtp, kDtlsSrtp };
class ChannelManagerTestWithRtpTransport
: public ChannelManagerTest,
public ::testing::WithParamInterface<RTPTransportType> {
public:
std::unique_ptr<webrtc::RtpTransportInternal> CreateRtpTransport() {
RTPTransportType type = GetParam();
switch (type) {
case RTPTransportType::kRtp:
return CreatePlainRtpTransport();
case RTPTransportType::kSrtp:
return CreateSrtpTransport();
case RTPTransportType::kDtlsSrtp:
return CreateDtlsSrtpTransport();
}
return nullptr;
}
void TestCreateDestroyChannels(webrtc::RtpTransportInternal* rtp_transport) {
cricket::VoiceChannel* voice_channel = cm_->CreateVoiceChannel(
&fake_call_, cricket::MediaConfig(), rtp_transport,
rtc::Thread::Current(), cricket::CN_AUDIO, kDefaultSrtpRequired,
AudioOptions());
EXPECT_TRUE(voice_channel != nullptr);
cricket::VideoChannel* video_channel = cm_->CreateVideoChannel(
&fake_call_, cricket::MediaConfig(), rtp_transport,
rtc::Thread::Current(), cricket::CN_VIDEO, kDefaultSrtpRequired,
VideoOptions());
EXPECT_TRUE(video_channel != nullptr);
cricket::RtpDataChannel* rtp_data_channel = cm_->CreateRtpDataChannel(
cricket::MediaConfig(), rtp_transport, rtc::Thread::Current(),
cricket::CN_DATA, kDefaultSrtpRequired);
EXPECT_TRUE(rtp_data_channel != nullptr);
cm_->DestroyVideoChannel(video_channel);
cm_->DestroyVoiceChannel(voice_channel);
cm_->DestroyRtpDataChannel(rtp_data_channel);
cm_->Terminate();
}
private:
std::unique_ptr<webrtc::RtpTransportInternal> CreatePlainRtpTransport() {
return rtc::MakeUnique<webrtc::RtpTransport>(/*rtcp_mux_required=*/true);
}
std::unique_ptr<webrtc::RtpTransportInternal> CreateSrtpTransport() {
auto rtp_transport =
rtc::MakeUnique<webrtc::RtpTransport>(/*rtcp_mux_required=*/true);
auto srtp_transport =
rtc::MakeUnique<webrtc::SrtpTransport>(std::move(rtp_transport));
return srtp_transport;
}
std::unique_ptr<webrtc::RtpTransportInternal> CreateDtlsSrtpTransport() {
auto rtp_transport =
rtc::MakeUnique<webrtc::RtpTransport>(/*rtcp_mux_required=*/true);
auto srtp_transport =
rtc::MakeUnique<webrtc::SrtpTransport>(std::move(rtp_transport));
auto dtls_srtp_transport_ =
rtc::MakeUnique<webrtc::DtlsSrtpTransport>(std::move(srtp_transport));
return dtls_srtp_transport_;
}
};
TEST_P(ChannelManagerTestWithRtpTransport, CreateDestroyChannels) {
TEST_F(ChannelManagerTest, CreateDestroyChannels) {
EXPECT_TRUE(cm_->Init());
auto rtp_transport = CreateRtpTransport();
auto rtp_transport = CreateDtlsSrtpTransport();
TestCreateDestroyChannels(rtp_transport.get());
}
TEST_P(ChannelManagerTestWithRtpTransport, CreateDestroyChannelsOnThread) {
TEST_F(ChannelManagerTest, CreateDestroyChannelsOnThread) {
network_->Start();
worker_->Start();
EXPECT_TRUE(cm_->set_worker_thread(worker_.get()));
EXPECT_TRUE(cm_->set_network_thread(network_.get()));
EXPECT_TRUE(cm_->Init());
auto rtp_transport = CreateRtpTransport();
auto rtp_transport = CreateDtlsSrtpTransport();
TestCreateDestroyChannels(rtp_transport.get());
}
INSTANTIATE_TEST_CASE_P(ChannelManagerTest,
ChannelManagerTestWithRtpTransport,
::testing::Values(RTPTransportType::kRtp,
RTPTransportType::kSrtp,
RTPTransportType::kDtlsSrtp));
} // namespace cricket

View File

@ -53,7 +53,7 @@ void DtlsSrtpTransport::SetDtlsTransports(
// When using DTLS-SRTP, we must reset the SrtpTransport every time the
// DtlsTransport changes and wait until the DTLS handshake is complete to set
// the newly negotiated parameters.
if (IsActive()) {
if (IsSrtpActive()) {
srtp_transport_->ResetParams();
}
@ -62,7 +62,7 @@ void DtlsSrtpTransport::SetDtlsTransports(
// This would only be possible if using BUNDLE but not rtcp-mux, which isn't
// allowed according to the BUNDLE spec.
RTC_CHECK(!(IsActive()))
RTC_CHECK(!(IsSrtpActive()))
<< "Setting RTCP for DTLS/SRTP after the DTLS is active "
"should never happen.";
@ -140,7 +140,7 @@ bool DtlsSrtpTransport::DtlsHandshakeCompleted() {
}
void DtlsSrtpTransport::MaybeSetupDtlsSrtp() {
if (IsActive() || !DtlsHandshakeCompleted()) {
if (IsSrtpActive() || !DtlsHandshakeCompleted()) {
return;
}
@ -184,7 +184,7 @@ void DtlsSrtpTransport::SetupRtcpDtlsSrtp() {
// Return if the DTLS-SRTP is active because the encrypted header extension
// IDs don't need to be updated for RTCP and the crypto params don't need to
// be reset.
if (IsActive()) {
if (IsSrtpActive()) {
return;
}

View File

@ -45,7 +45,7 @@ class DtlsSrtpTransport : public RtpTransportInternalAdapter {
void UpdateRecvEncryptedHeaderExtensionIds(
const std::vector<int>& recv_extension_ids);
bool IsActive() { return srtp_transport_->IsActive(); }
bool IsSrtpActive() const override { return srtp_transport_->IsSrtpActive(); }
// Cache RTP Absoulute SendTime extension header ID. This is only used when
// external authentication is enabled.
@ -54,11 +54,17 @@ class DtlsSrtpTransport : public RtpTransportInternalAdapter {
rtp_abs_sendtime_extn_id);
}
// TODO(zhihuang): Remove this when we remove RtpTransportAdapter.
RtpTransportAdapter* GetInternal() override { return nullptr; }
sigslot::signal2<DtlsSrtpTransport*, bool> SignalDtlsSrtpSetupFailure;
RTCError SetSrtpSendKey(const cricket::CryptoParams& params) override {
return RTCError(RTCErrorType::UNSUPPORTED_OPERATION,
"Set SRTP keys for DTLS-SRTP is not supported.");
}
RTCError SetSrtpReceiveKey(const cricket::CryptoParams& params) override {
return RTCError(RTCErrorType::UNSUPPORTED_OPERATION,
"Set SRTP keys for DTLS-SRTP is not supported.");
}
private:
bool IsDtlsActive();
bool IsDtlsConnected();

View File

@ -70,9 +70,6 @@ class DtlsSrtpTransportTest : public testing::Test,
bool rtcp_mux_enabled) {
auto rtp_transport = rtc::MakeUnique<RtpTransport>(rtcp_mux_enabled);
rtp_transport->AddHandledPayloadType(0x00);
rtp_transport->AddHandledPayloadType(0xc9);
auto srtp_transport =
rtc::MakeUnique<SrtpTransport>(std::move(rtp_transport));
auto dtls_srtp_transport =
@ -118,8 +115,8 @@ class DtlsSrtpTransportTest : public testing::Test,
void SendRecvRtpPackets() {
ASSERT_TRUE(dtls_srtp_transport1_);
ASSERT_TRUE(dtls_srtp_transport2_);
ASSERT_TRUE(dtls_srtp_transport1_->IsActive());
ASSERT_TRUE(dtls_srtp_transport2_->IsActive());
ASSERT_TRUE(dtls_srtp_transport1_->IsSrtpActive());
ASSERT_TRUE(dtls_srtp_transport2_->IsSrtpActive());
size_t rtp_len = sizeof(kPcmuFrame);
size_t packet_size = rtp_len + kRtpAuthTagLen;
@ -181,8 +178,8 @@ class DtlsSrtpTransportTest : public testing::Test,
const std::vector<int>& encrypted_header_ids) {
ASSERT_TRUE(dtls_srtp_transport1_);
ASSERT_TRUE(dtls_srtp_transport2_);
ASSERT_TRUE(dtls_srtp_transport1_->IsActive());
ASSERT_TRUE(dtls_srtp_transport2_->IsActive());
ASSERT_TRUE(dtls_srtp_transport1_->IsSrtpActive());
ASSERT_TRUE(dtls_srtp_transport2_->IsSrtpActive());
size_t rtp_len = sizeof(kPcmuFrameWithExtensions);
size_t packet_size = rtp_len + kRtpAuthTagLen;
@ -353,8 +350,8 @@ TEST_F(DtlsSrtpTransportTest,
rtcp_dtls2.get(), /*rtcp_mux_enabled=*/false);
CompleteDtlsHandshake(rtp_dtls1.get(), rtp_dtls2.get());
EXPECT_FALSE(dtls_srtp_transport1_->IsActive());
EXPECT_FALSE(dtls_srtp_transport2_->IsActive());
EXPECT_FALSE(dtls_srtp_transport1_->IsSrtpActive());
EXPECT_FALSE(dtls_srtp_transport2_->IsSrtpActive());
CompleteDtlsHandshake(rtcp_dtls1.get(), rtcp_dtls2.get());
SendRecvPackets();
}
@ -372,8 +369,8 @@ TEST_F(DtlsSrtpTransportTest, DtlsSrtpResetAfterDtlsTransportChange) {
/*rtcp_mux_enabled=*/true);
CompleteDtlsHandshake(rtp_dtls1.get(), rtp_dtls2.get());
EXPECT_TRUE(dtls_srtp_transport1_->IsActive());
EXPECT_TRUE(dtls_srtp_transport2_->IsActive());
EXPECT_TRUE(dtls_srtp_transport1_->IsSrtpActive());
EXPECT_TRUE(dtls_srtp_transport2_->IsSrtpActive());
auto rtp_dtls3 = rtc::MakeUnique<FakeDtlsTransport>(
"audio", cricket::ICE_CANDIDATE_COMPONENT_RTP);
@ -383,8 +380,8 @@ TEST_F(DtlsSrtpTransportTest, DtlsSrtpResetAfterDtlsTransportChange) {
// The previous context is reset.
dtls_srtp_transport1_->SetDtlsTransports(rtp_dtls3.get(), nullptr);
dtls_srtp_transport2_->SetDtlsTransports(rtp_dtls4.get(), nullptr);
EXPECT_FALSE(dtls_srtp_transport1_->IsActive());
EXPECT_FALSE(dtls_srtp_transport2_->IsActive());
EXPECT_FALSE(dtls_srtp_transport1_->IsSrtpActive());
EXPECT_FALSE(dtls_srtp_transport2_->IsSrtpActive());
// Re-setup.
CompleteDtlsHandshake(rtp_dtls3.get(), rtp_dtls4.get());
@ -409,8 +406,8 @@ TEST_F(DtlsSrtpTransportTest,
CompleteDtlsHandshake(rtp_dtls1.get(), rtp_dtls2.get());
// Inactive because the RTCP transport handshake didn't complete.
EXPECT_FALSE(dtls_srtp_transport1_->IsActive());
EXPECT_FALSE(dtls_srtp_transport2_->IsActive());
EXPECT_FALSE(dtls_srtp_transport1_->IsSrtpActive());
EXPECT_FALSE(dtls_srtp_transport2_->IsSrtpActive());
dtls_srtp_transport1_->SetRtcpMuxEnabled(true);
dtls_srtp_transport2_->SetRtcpMuxEnabled(true);

View File

@ -1,428 +0,0 @@
/*
* Copyright 2004 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/jseptransport.h"
#include <memory>
#include <utility> // for std::pair
#include "api/candidate.h"
#include "p2p/base/p2pconstants.h"
#include "p2p/base/p2ptransportchannel.h"
#include "p2p/base/port.h"
#include "rtc_base/bind.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
using webrtc::SdpType;
namespace cricket {
static bool BadTransportDescription(const std::string& desc,
std::string* err_desc) {
if (err_desc) {
*err_desc = desc;
}
RTC_LOG(LS_ERROR) << desc;
return false;
}
static bool VerifyIceParams(const TransportDescription& desc) {
// For legacy protocols.
if (desc.ice_ufrag.empty() && desc.ice_pwd.empty())
return true;
if (desc.ice_ufrag.length() < ICE_UFRAG_MIN_LENGTH ||
desc.ice_ufrag.length() > ICE_UFRAG_MAX_LENGTH) {
return false;
}
if (desc.ice_pwd.length() < ICE_PWD_MIN_LENGTH ||
desc.ice_pwd.length() > ICE_PWD_MAX_LENGTH) {
return false;
}
return true;
}
JsepTransport::JsepTransport(
const std::string& mid,
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate)
: mid_(mid), certificate_(certificate) {}
JsepTransport::~JsepTransport() = default;
bool JsepTransport::AddChannel(DtlsTransportInternal* dtls, int component) {
if (channels_.find(component) != channels_.end()) {
RTC_LOG(LS_ERROR) << "Adding channel for component " << component
<< " twice.";
return false;
}
channels_[component] = dtls;
// Something's wrong if a channel is being added after a description is set.
// This may currently occur if rtcp-mux is negotiated, then a new m= section
// is added in a later offer/answer. But this is suboptimal and should be
// changed; we shouldn't support going from muxed to non-muxed.
// TODO(deadbeef): Once this is fixed, make the warning an error, and remove
// the calls to "ApplyXTransportDescription" below.
if (local_description_set_ || remote_description_set_) {
RTC_LOG(LS_WARNING) << "Adding new transport channel after "
"transport description already applied.";
}
bool ret = true;
std::string err;
if (local_description_set_) {
ret &= ApplyLocalTransportDescription(channels_[component], &err);
}
if (remote_description_set_) {
ret &= ApplyRemoteTransportDescription(channels_[component], &err);
}
if (local_description_set_ && remote_description_set_) {
ret &= ApplyNegotiatedTransportDescription(channels_[component], &err);
}
return ret;
}
bool JsepTransport::RemoveChannel(int component) {
auto it = channels_.find(component);
if (it == channels_.end()) {
RTC_LOG(LS_ERROR) << "Trying to remove channel for component " << component
<< ", which doesn't exist.";
return false;
}
channels_.erase(component);
return true;
}
bool JsepTransport::HasChannels() const {
return !channels_.empty();
}
void JsepTransport::SetLocalCertificate(
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) {
certificate_ = certificate;
}
bool JsepTransport::GetLocalCertificate(
rtc::scoped_refptr<rtc::RTCCertificate>* certificate) const {
if (!certificate_) {
return false;
}
*certificate = certificate_;
return true;
}
bool JsepTransport::SetLocalTransportDescription(
const TransportDescription& description,
SdpType type,
std::string* error_desc) {
bool ret = true;
if (!VerifyIceParams(description)) {
return BadTransportDescription("Invalid ice-ufrag or ice-pwd length",
error_desc);
}
bool ice_restarting =
local_description_set_ &&
IceCredentialsChanged(local_description_->ice_ufrag,
local_description_->ice_pwd, description.ice_ufrag,
description.ice_pwd);
local_description_.reset(new TransportDescription(description));
rtc::SSLFingerprint* local_fp =
local_description_->identity_fingerprint.get();
if (!local_fp) {
certificate_ = nullptr;
} else if (!VerifyCertificateFingerprint(certificate_.get(), local_fp,
error_desc)) {
return false;
}
for (const auto& kv : channels_) {
ret &= ApplyLocalTransportDescription(kv.second, error_desc);
}
if (!ret) {
return false;
}
// If PRANSWER/ANSWER is set, we should decide transport protocol type.
if (type == SdpType::kPrAnswer || type == SdpType::kAnswer) {
ret &= NegotiateTransportDescription(type, error_desc);
}
if (!ret) {
return false;
}
if (needs_ice_restart_ && ice_restarting) {
needs_ice_restart_ = false;
RTC_LOG(LS_VERBOSE) << "needs-ice-restart flag cleared for transport "
<< mid();
}
local_description_set_ = true;
return true;
}
bool JsepTransport::SetRemoteTransportDescription(
const TransportDescription& description,
SdpType type,
std::string* error_desc) {
bool ret = true;
if (!VerifyIceParams(description)) {
return BadTransportDescription("Invalid ice-ufrag or ice-pwd length",
error_desc);
}
remote_description_.reset(new TransportDescription(description));
for (const auto& kv : channels_) {
ret &= ApplyRemoteTransportDescription(kv.second, error_desc);
}
// If PRANSWER/ANSWER is set, we should decide transport protocol type.
if (type == SdpType::kPrAnswer || type == SdpType::kAnswer) {
ret = NegotiateTransportDescription(SdpType::kOffer, error_desc);
}
if (ret) {
remote_description_set_ = true;
}
return ret;
}
void JsepTransport::SetNeedsIceRestartFlag() {
if (!needs_ice_restart_) {
needs_ice_restart_ = true;
RTC_LOG(LS_VERBOSE) << "needs-ice-restart flag set for transport " << mid();
}
}
bool JsepTransport::NeedsIceRestart() const {
return needs_ice_restart_;
}
rtc::Optional<rtc::SSLRole> JsepTransport::GetSslRole() const {
return ssl_role_;
}
bool JsepTransport::GetStats(TransportStats* stats) {
stats->transport_name = mid();
stats->channel_stats.clear();
for (auto& kv : channels_) {
DtlsTransportInternal* dtls_transport = kv.second;
TransportChannelStats substats;
substats.component = kv.first;
dtls_transport->GetSrtpCryptoSuite(&substats.srtp_crypto_suite);
dtls_transport->GetSslCipherSuite(&substats.ssl_cipher_suite);
substats.dtls_state = dtls_transport->dtls_state();
if (!dtls_transport->ice_transport()->GetStats(
&substats.connection_infos, &substats.candidate_stats_list)) {
return false;
}
stats->channel_stats.push_back(substats);
}
return true;
}
bool JsepTransport::VerifyCertificateFingerprint(
const rtc::RTCCertificate* certificate,
const rtc::SSLFingerprint* fingerprint,
std::string* error_desc) const {
if (!fingerprint) {
return BadTransportDescription("No fingerprint.", error_desc);
}
if (!certificate) {
return BadTransportDescription(
"Fingerprint provided but no identity available.", error_desc);
}
std::unique_ptr<rtc::SSLFingerprint> fp_tmp(rtc::SSLFingerprint::Create(
fingerprint->algorithm, certificate->identity()));
RTC_DCHECK(fp_tmp.get() != NULL);
if (*fp_tmp == *fingerprint) {
return true;
}
std::ostringstream desc;
desc << "Local fingerprint does not match identity. Expected: ";
desc << fp_tmp->ToString();
desc << " Got: " << fingerprint->ToString();
return BadTransportDescription(desc.str(), error_desc);
}
bool JsepTransport::ApplyLocalTransportDescription(
DtlsTransportInternal* dtls_transport,
std::string* error_desc) {
dtls_transport->ice_transport()->SetIceParameters(
local_description_->GetIceParameters());
return true;
}
bool JsepTransport::ApplyRemoteTransportDescription(
DtlsTransportInternal* dtls_transport,
std::string* error_desc) {
dtls_transport->ice_transport()->SetRemoteIceParameters(
remote_description_->GetIceParameters());
dtls_transport->ice_transport()->SetRemoteIceMode(
remote_description_->ice_mode);
return true;
}
bool JsepTransport::ApplyNegotiatedTransportDescription(
DtlsTransportInternal* dtls_transport,
std::string* error_desc) {
// Set SSL role. Role must be set before fingerprint is applied, which
// initiates DTLS setup.
if (ssl_role_ && !dtls_transport->SetDtlsRole(*ssl_role_)) {
return BadTransportDescription("Failed to set SSL role for the channel.",
error_desc);
}
// Apply remote fingerprint.
if (!dtls_transport->SetRemoteFingerprint(
remote_fingerprint_->algorithm,
reinterpret_cast<const uint8_t*>(remote_fingerprint_->digest.data()),
remote_fingerprint_->digest.size())) {
return BadTransportDescription("Failed to apply remote fingerprint.",
error_desc);
}
return true;
}
bool JsepTransport::NegotiateTransportDescription(
SdpType local_description_type,
std::string* error_desc) {
if (!local_description_ || !remote_description_) {
const std::string msg =
"Applying an answer transport description "
"without applying any offer.";
return BadTransportDescription(msg, error_desc);
}
rtc::SSLFingerprint* local_fp =
local_description_->identity_fingerprint.get();
rtc::SSLFingerprint* remote_fp =
remote_description_->identity_fingerprint.get();
if (remote_fp && local_fp) {
remote_fingerprint_.reset(new rtc::SSLFingerprint(*remote_fp));
if (!NegotiateRole(local_description_type, error_desc)) {
return false;
}
} else if (local_fp && (local_description_type == SdpType::kAnswer)) {
return BadTransportDescription(
"Local fingerprint supplied when caller didn't offer DTLS.",
error_desc);
} else {
// We are not doing DTLS
remote_fingerprint_.reset(new rtc::SSLFingerprint("", nullptr, 0));
}
// Now that we have negotiated everything, push it downward.
// Note that we cache the result so that if we have race conditions
// between future SetRemote/SetLocal invocations and new channel
// creation, we have the negotiation state saved until a new
// negotiation happens.
for (const auto& kv : channels_) {
if (!ApplyNegotiatedTransportDescription(kv.second, error_desc)) {
return false;
}
}
return true;
}
bool JsepTransport::NegotiateRole(SdpType local_description_type,
std::string* error_desc) {
if (!local_description_ || !remote_description_) {
const std::string msg =
"Local and Remote description must be set before "
"transport descriptions are negotiated";
return BadTransportDescription(msg, error_desc);
}
// From RFC 4145, section-4.1, The following are the values that the
// 'setup' attribute can take in an offer/answer exchange:
// Offer Answer
// ________________
// active passive / holdconn
// passive active / holdconn
// actpass active / passive / holdconn
// holdconn holdconn
//
// Set the role that is most conformant with RFC 5763, Section 5, bullet 1
// The endpoint MUST use the setup attribute defined in [RFC4145].
// The endpoint that is the offerer MUST use the setup attribute
// value of setup:actpass and be prepared to receive a client_hello
// before it receives the answer. The answerer MUST use either a
// setup attribute value of setup:active or setup:passive. Note that
// if the answerer uses setup:passive, then the DTLS handshake will
// not begin until the answerer is received, which adds additional
// latency. setup:active allows the answer and the DTLS handshake to
// occur in parallel. Thus, setup:active is RECOMMENDED. Whichever
// party is active MUST initiate a DTLS handshake by sending a
// ClientHello over each flow (host/port quartet).
// IOW - actpass and passive modes should be treated as server and
// active as client.
ConnectionRole local_connection_role = local_description_->connection_role;
ConnectionRole remote_connection_role = remote_description_->connection_role;
bool is_remote_server = false;
if (local_description_type == SdpType::kOffer) {
if (local_connection_role != CONNECTIONROLE_ACTPASS) {
return BadTransportDescription(
"Offerer must use actpass value for setup attribute.", error_desc);
}
if (remote_connection_role == CONNECTIONROLE_ACTIVE ||
remote_connection_role == CONNECTIONROLE_PASSIVE ||
remote_connection_role == CONNECTIONROLE_NONE) {
is_remote_server = (remote_connection_role == CONNECTIONROLE_PASSIVE);
} else {
const std::string msg =
"Answerer must use either active or passive value "
"for setup attribute.";
return BadTransportDescription(msg, error_desc);
}
// If remote is NONE or ACTIVE it will act as client.
} else {
if (remote_connection_role != CONNECTIONROLE_ACTPASS &&
remote_connection_role != CONNECTIONROLE_NONE) {
// Accept a remote role attribute that's not "actpass", but matches the
// current negotiated role. This is allowed by dtls-sdp, though our
// implementation will never generate such an offer as it's not
// recommended.
//
// See https://datatracker.ietf.org/doc/html/draft-ietf-mmusic-dtls-sdp,
// section 5.5.
if (!ssl_role_ ||
(*ssl_role_ == rtc::SSL_CLIENT &&
remote_connection_role == CONNECTIONROLE_ACTIVE) ||
(*ssl_role_ == rtc::SSL_SERVER &&
remote_connection_role == CONNECTIONROLE_PASSIVE)) {
return BadTransportDescription(
"Offerer must use actpass value or current negotiated role for "
"setup attribute.",
error_desc);
}
}
if (local_connection_role == CONNECTIONROLE_ACTIVE ||
local_connection_role == CONNECTIONROLE_PASSIVE) {
is_remote_server = (local_connection_role == CONNECTIONROLE_ACTIVE);
} else {
const std::string msg =
"Answerer must use either active or passive value "
"for setup attribute.";
return BadTransportDescription(msg, error_desc);
}
// If local is passive, local will act as server.
}
ssl_role_.emplace(is_remote_server ? rtc::SSL_CLIENT : rtc::SSL_SERVER);
return true;
}
} // namespace cricket

View File

@ -1,179 +0,0 @@
/*
* Copyright 2004 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.
*/
#ifndef PC_JSEPTRANSPORT_H_
#define PC_JSEPTRANSPORT_H_
#include <map>
#include <memory>
#include <string>
#include <vector>
#include "api/candidate.h"
#include "api/jsep.h"
#include "api/optional.h"
#include "p2p/base/dtlstransport.h"
#include "p2p/base/p2pconstants.h"
#include "p2p/base/transportinfo.h"
#include "pc/sessiondescription.h"
#include "pc/transportstats.h"
#include "rtc_base/constructormagic.h"
#include "rtc_base/messagequeue.h"
#include "rtc_base/rtccertificate.h"
#include "rtc_base/sigslot.h"
#include "rtc_base/sslstreamadapter.h"
namespace cricket {
class DtlsTransportInternal;
// Helper class used by TransportController that processes
// TransportDescriptions. A TransportDescription represents the
// transport-specific properties of an SDP m= section, processed according to
// JSEP. Each transport consists of DTLS and ICE transport channels for RTP
// (and possibly RTCP, if rtcp-mux isn't used).
//
// On Threading: Transport performs work solely on the network thread, and so
// its methods should only be called on the network thread.
class JsepTransport : public sigslot::has_slots<> {
public:
// |mid| is just used for log statements in order to identify the Transport.
// Note that |certificate| is allowed to be null since a remote description
// may be set before a local certificate is generated.
JsepTransport(const std::string& mid,
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate);
~JsepTransport() override;
// Returns the MID of this transport.
const std::string& mid() const { return mid_; }
// Add or remove channel that is affected when a local/remote transport
// description is set on this transport. Need to add all channels before
// setting a transport description.
bool AddChannel(DtlsTransportInternal* dtls, int component);
bool RemoveChannel(int component);
bool HasChannels() const;
bool ready_for_remote_candidates() const {
return local_description_set_ && remote_description_set_;
}
// Must be called before applying local session description.
// Needed in order to verify the local fingerprint.
void SetLocalCertificate(
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate);
// Get a copy of the local certificate provided by SetLocalCertificate.
bool GetLocalCertificate(
rtc::scoped_refptr<rtc::RTCCertificate>* certificate) const;
// Set the local TransportDescription to be used by DTLS and ICE channels
// that are part of this Transport.
bool SetLocalTransportDescription(const TransportDescription& description,
webrtc::SdpType type,
std::string* error_desc);
// Set the remote TransportDescription to be used by DTLS and ICE channels
// that are part of this Transport.
bool SetRemoteTransportDescription(const TransportDescription& description,
webrtc::SdpType type,
std::string* error_desc);
// Set the "needs-ice-restart" flag as described in JSEP. After the flag is
// set, offers should generate new ufrags/passwords until an ICE restart
// occurs.
//
// This and the below method can be called safely from any thread as long as
// SetXTransportDescription is not in progress.
void SetNeedsIceRestartFlag();
// Returns true if the ICE restart flag above was set, and no ICE restart has
// occurred yet for this transport (by applying a local description with
// changed ufrag/password).
bool NeedsIceRestart() const;
// Returns role if negotiated, or empty Optional if it hasn't been negotiated
// yet.
rtc::Optional<rtc::SSLRole> GetSslRole() const;
// TODO(deadbeef): Make this const. See comment in transportcontroller.h.
bool GetStats(TransportStats* stats);
// The current local transport description, possibly used
// by the transport controller.
const TransportDescription* local_description() const {
return local_description_.get();
}
// The current remote transport description, possibly used
// by the transport controller.
const TransportDescription* remote_description() const {
return remote_description_.get();
}
// TODO(deadbeef): The methods below are only public for testing. Should make
// them utility functions or objects so they can be tested independently from
// this class.
// Returns false if the certificate's identity does not match the fingerprint,
// or either is NULL.
bool VerifyCertificateFingerprint(const rtc::RTCCertificate* certificate,
const rtc::SSLFingerprint* fingerprint,
std::string* error_desc) const;
private:
// Negotiates the transport parameters based on the current local and remote
// transport description, such as the ICE role to use, and whether DTLS
// should be activated.
//
// Called when an answer TransportDescription is applied.
bool NegotiateTransportDescription(webrtc::SdpType local_description_type,
std::string* error_desc);
// Negotiates the SSL role based off the offer and answer as specified by
// RFC 4145, section-4.1. Returns false if the SSL role cannot be determined
// from the local description and remote description.
bool NegotiateRole(webrtc::SdpType local_description_type,
std::string* error_desc);
// Pushes down the transport parameters from the local description, such
// as the ICE ufrag and pwd.
bool ApplyLocalTransportDescription(DtlsTransportInternal* dtls_transport,
std::string* error_desc);
// Pushes down the transport parameters from the remote description to the
// transport channel.
bool ApplyRemoteTransportDescription(DtlsTransportInternal* dtls_transport,
std::string* error_desc);
// Pushes down the transport parameters obtained via negotiation.
bool ApplyNegotiatedTransportDescription(
DtlsTransportInternal* dtls_transport,
std::string* error_desc);
const std::string mid_;
// needs-ice-restart bit as described in JSEP.
bool needs_ice_restart_ = false;
rtc::scoped_refptr<rtc::RTCCertificate> certificate_;
rtc::Optional<rtc::SSLRole> ssl_role_;
std::unique_ptr<rtc::SSLFingerprint> remote_fingerprint_;
std::unique_ptr<TransportDescription> local_description_;
std::unique_ptr<TransportDescription> remote_description_;
bool local_description_set_ = false;
bool remote_description_set_ = false;
// Candidate component => DTLS channel
std::map<int, DtlsTransportInternal*> channels_;
RTC_DISALLOW_COPY_AND_ASSIGN(JsepTransport);
};
} // namespace cricket
#endif // PC_JSEPTRANSPORT_H_

View File

@ -54,10 +54,12 @@ JsepTransportDescription::JsepTransportDescription(
bool rtcp_mux_enabled,
const std::vector<CryptoParams>& cryptos,
const std::vector<int>& encrypted_header_extension_ids,
int rtp_abs_sendtime_extn_id,
const TransportDescription& transport_desc)
: rtcp_mux_enabled(rtcp_mux_enabled),
cryptos(cryptos),
encrypted_header_extension_ids(encrypted_header_extension_ids),
rtp_abs_sendtime_extn_id(rtp_abs_sendtime_extn_id),
transport_desc(transport_desc) {}
JsepTransportDescription::JsepTransportDescription(
@ -65,6 +67,7 @@ JsepTransportDescription::JsepTransportDescription(
: rtcp_mux_enabled(from.rtcp_mux_enabled),
cryptos(from.cryptos),
encrypted_header_extension_ids(from.encrypted_header_extension_ids),
rtp_abs_sendtime_extn_id(from.rtp_abs_sendtime_extn_id),
transport_desc(from.transport_desc) {}
JsepTransportDescription::~JsepTransportDescription() = default;
@ -77,6 +80,7 @@ JsepTransportDescription& JsepTransportDescription::operator=(
rtcp_mux_enabled = from.rtcp_mux_enabled;
cryptos = from.cryptos;
encrypted_header_extension_ids = from.encrypted_header_extension_ids;
rtp_abs_sendtime_extn_id = from.rtp_abs_sendtime_extn_id;
transport_desc = from.transport_desc;
return *this;
@ -218,11 +222,15 @@ webrtc::RTCError JsepTransport2::SetRemoteJsepTransportDescription(
return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER,
"Failed to setup SDES crypto parameters.");
}
sdes_transport_->CacheRtpAbsSendTimeHeaderExtension(
jsep_description.rtp_abs_sendtime_extn_id);
} else if (dtls_srtp_transport_) {
RTC_DCHECK(!unencrypted_rtp_transport_);
RTC_DCHECK(!sdes_transport_);
dtls_srtp_transport_->UpdateSendEncryptedHeaderExtensionIds(
jsep_description.encrypted_header_extension_ids);
dtls_srtp_transport_->CacheRtpAbsSendTimeHeaderExtension(
jsep_description.rtp_abs_sendtime_extn_id);
}
remote_description_.reset(new JsepTransportDescription(jsep_description));

View File

@ -46,6 +46,7 @@ struct JsepTransportDescription {
bool rtcp_mux_enabled,
const std::vector<CryptoParams>& cryptos,
const std::vector<int>& encrypted_header_extension_ids,
int rtp_abs_sendtime_extn_id,
const TransportDescription& transport_description);
JsepTransportDescription(const JsepTransportDescription& from);
~JsepTransportDescription();
@ -55,6 +56,7 @@ struct JsepTransportDescription {
bool rtcp_mux_enabled = true;
std::vector<CryptoParams> cryptos;
std::vector<int> encrypted_header_extension_ids;
int rtp_abs_sendtime_extn_id = -1;
// TODO(zhihuang): Add the ICE and DTLS related variables and methods from
// TransportDescription and remove this extra layer of abstraction.
TransportDescription transport_desc;

View File

@ -9,8 +9,10 @@
*/
#include <memory>
#include <tuple>
#include <utility>
#include "media/base/fakertp.h"
#include "p2p/base/fakedtlstransport.h"
#include "p2p/base/fakeicetransport.h"
#include "pc/jseptransport2.h"
@ -40,7 +42,6 @@ struct NegotiateRoleParams {
class JsepTransport2Test : public testing::Test, public sigslot::has_slots<> {
protected:
std::unique_ptr<webrtc::SrtpTransport> CreateSdesTransport(
const std::string& transport_name,
rtc::PacketTransportInternal* rtp_packet_transport,
rtc::PacketTransportInternal* rtcp_packet_transport) {
bool rtcp_mux_enabled = (rtcp_packet_transport == nullptr);
@ -55,7 +56,6 @@ class JsepTransport2Test : public testing::Test, public sigslot::has_slots<> {
}
std::unique_ptr<webrtc::DtlsSrtpTransport> CreateDtlsSrtpTransport(
const std::string& transport_name,
cricket::DtlsTransportInternal* rtp_dtls_transport,
cricket::DtlsTransportInternal* rtcp_dtls_transport) {
bool rtcp_mux_enabled = (rtcp_dtls_transport == nullptr);
@ -71,7 +71,8 @@ class JsepTransport2Test : public testing::Test, public sigslot::has_slots<> {
// Create a new JsepTransport2 with a FakeDtlsTransport and a
// FakeIceTransport.
void CreateJsepTransport2(bool rtcp_mux_enabled, SrtpMode srtp_mode) {
std::unique_ptr<JsepTransport2> CreateJsepTransport2(bool rtcp_mux_enabled,
SrtpMode srtp_mode) {
auto ice = rtc::MakeUnique<FakeIceTransport>(kTransportName,
ICE_CANDIDATE_COMPONENT_RTP);
auto rtp_dtls_transport =
@ -89,29 +90,28 @@ class JsepTransport2Test : public testing::Test, public sigslot::has_slots<> {
std::unique_ptr<webrtc::DtlsSrtpTransport> dtls_srtp_transport;
switch (srtp_mode) {
case SrtpMode::kSdes:
sdes_transport =
CreateSdesTransport(kTransportName, rtp_dtls_transport.get(),
rtcp_dtls_transport.get());
sdes_transport = CreateSdesTransport(rtp_dtls_transport.get(),
rtcp_dtls_transport.get());
sdes_transport_ = sdes_transport.get();
break;
case SrtpMode::kDtlsSrtp:
dtls_srtp_transport =
CreateDtlsSrtpTransport(kTransportName, rtp_dtls_transport.get(),
rtcp_dtls_transport.get());
dtls_srtp_transport = CreateDtlsSrtpTransport(
rtp_dtls_transport.get(), rtcp_dtls_transport.get());
break;
default:
RTC_NOTREACHED();
}
jsep_transport_ = rtc::MakeUnique<JsepTransport2>(
auto jsep_transport = rtc::MakeUnique<JsepTransport2>(
kTransportName, /*local_certificate=*/nullptr,
std::move(unencrypted_rtp_transport), std::move(sdes_transport),
std::move(dtls_srtp_transport), std::move(rtp_dtls_transport),
std::move(rtcp_dtls_transport));
signal_rtcp_mux_active_received_ = false;
jsep_transport_->SignalRtcpMuxActive.connect(
jsep_transport->SignalRtcpMuxActive.connect(
this, &JsepTransport2Test::OnRtcpMuxActive);
return jsep_transport;
}
JsepTransportDescription MakeJsepTransportDescription(
@ -159,7 +159,7 @@ class JsepTransport2WithRtcpMux : public JsepTransport2Test,
// This test verifies the ICE parameters are properly applied to the transports.
TEST_P(JsepTransport2WithRtcpMux, SetIceParameters) {
bool rtcp_mux_enabled = GetParam();
CreateJsepTransport2(rtcp_mux_enabled, SrtpMode::kDtlsSrtp);
jsep_transport_ = CreateJsepTransport2(rtcp_mux_enabled, SrtpMode::kDtlsSrtp);
JsepTransportDescription jsep_description;
jsep_description.transport_desc = TransportDescription(kIceUfrag1, kIcePwd1);
@ -205,7 +205,7 @@ TEST_P(JsepTransport2WithRtcpMux, SetIceParameters) {
// Similarly, test DTLS parameters are properly applied to the transports.
TEST_P(JsepTransport2WithRtcpMux, SetDtlsParameters) {
bool rtcp_mux_enabled = GetParam();
CreateJsepTransport2(rtcp_mux_enabled, SrtpMode::kDtlsSrtp);
jsep_transport_ = CreateJsepTransport2(rtcp_mux_enabled, SrtpMode::kDtlsSrtp);
// Create certificates.
rtc::scoped_refptr<rtc::RTCCertificate> local_cert =
@ -256,7 +256,7 @@ TEST_P(JsepTransport2WithRtcpMux, SetDtlsParameters) {
// CONNECTIONROLE_PASSIVE, expecting SSL_CLIENT role.
TEST_P(JsepTransport2WithRtcpMux, SetDtlsParametersWithPassiveAnswer) {
bool rtcp_mux_enabled = GetParam();
CreateJsepTransport2(rtcp_mux_enabled, SrtpMode::kDtlsSrtp);
jsep_transport_ = CreateJsepTransport2(rtcp_mux_enabled, SrtpMode::kDtlsSrtp);
// Create certificates.
rtc::scoped_refptr<rtc::RTCCertificate> local_cert =
@ -308,7 +308,7 @@ TEST_P(JsepTransport2WithRtcpMux, SetDtlsParametersWithPassiveAnswer) {
// only starts returning "false" once an ICE restart has been initiated.
TEST_P(JsepTransport2WithRtcpMux, NeedsIceRestart) {
bool rtcp_mux_enabled = GetParam();
CreateJsepTransport2(rtcp_mux_enabled, SrtpMode::kDtlsSrtp);
jsep_transport_ = CreateJsepTransport2(rtcp_mux_enabled, SrtpMode::kDtlsSrtp);
// Use the same JsepTransportDescription for both offer and answer.
JsepTransportDescription description;
@ -353,7 +353,7 @@ TEST_P(JsepTransport2WithRtcpMux, NeedsIceRestart) {
TEST_P(JsepTransport2WithRtcpMux, GetStats) {
bool rtcp_mux_enabled = GetParam();
CreateJsepTransport2(rtcp_mux_enabled, SrtpMode::kDtlsSrtp);
jsep_transport_ = CreateJsepTransport2(rtcp_mux_enabled, SrtpMode::kDtlsSrtp);
size_t expected_stats_size = rtcp_mux_enabled ? 1u : 2u;
TransportStats stats;
@ -369,7 +369,7 @@ TEST_P(JsepTransport2WithRtcpMux, GetStats) {
// certificate matches the fingerprint.
TEST_P(JsepTransport2WithRtcpMux, VerifyCertificateFingerprint) {
bool rtcp_mux_enabled = GetParam();
CreateJsepTransport2(rtcp_mux_enabled, SrtpMode::kDtlsSrtp);
jsep_transport_ = CreateJsepTransport2(rtcp_mux_enabled, SrtpMode::kDtlsSrtp);
EXPECT_FALSE(
jsep_transport_->VerifyCertificateFingerprint(nullptr, nullptr).ok());
@ -436,7 +436,8 @@ TEST_P(JsepTransport2WithRtcpMux, ValidDtlsRoleNegotiation) {
SdpType::kPrAnswer}};
for (auto& param : valid_client_params) {
CreateJsepTransport2(rtcp_mux_enabled, SrtpMode::kDtlsSrtp);
jsep_transport_ =
CreateJsepTransport2(rtcp_mux_enabled, SrtpMode::kDtlsSrtp);
jsep_transport_->SetLocalCertificate(certificate);
local_description.transport_desc.connection_role = param.local_role;
@ -477,7 +478,8 @@ TEST_P(JsepTransport2WithRtcpMux, ValidDtlsRoleNegotiation) {
SdpType::kPrAnswer}};
for (auto& param : valid_server_params) {
CreateJsepTransport2(rtcp_mux_enabled, SrtpMode::kDtlsSrtp);
jsep_transport_ =
CreateJsepTransport2(rtcp_mux_enabled, SrtpMode::kDtlsSrtp);
jsep_transport_->SetLocalCertificate(certificate);
local_description.transport_desc.connection_role = param.local_role;
@ -548,7 +550,8 @@ TEST_P(JsepTransport2WithRtcpMux, InvalidDtlsRoleNegotiation) {
SdpType::kPrAnswer}};
for (auto& param : duplicate_params) {
CreateJsepTransport2(rtcp_mux_enabled, SrtpMode::kDtlsSrtp);
jsep_transport_ =
CreateJsepTransport2(rtcp_mux_enabled, SrtpMode::kDtlsSrtp);
jsep_transport_->SetLocalCertificate(certificate);
local_description.transport_desc.connection_role = param.local_role;
@ -603,7 +606,8 @@ TEST_P(JsepTransport2WithRtcpMux, InvalidDtlsRoleNegotiation) {
SdpType::kPrAnswer}};
for (auto& param : offerer_without_actpass_params) {
CreateJsepTransport2(rtcp_mux_enabled, SrtpMode::kDtlsSrtp);
jsep_transport_ =
CreateJsepTransport2(rtcp_mux_enabled, SrtpMode::kDtlsSrtp);
jsep_transport_->SetLocalCertificate(certificate);
local_description.transport_desc.connection_role = param.local_role;
@ -645,7 +649,7 @@ TEST_F(JsepTransport2Test, ValidDtlsReofferFromAnswerer) {
rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
rtc::SSLIdentity::Generate("testing", rtc::KT_ECDSA)));
bool rtcp_mux_enabled = true;
CreateJsepTransport2(rtcp_mux_enabled, SrtpMode::kDtlsSrtp);
jsep_transport_ = CreateJsepTransport2(rtcp_mux_enabled, SrtpMode::kDtlsSrtp);
jsep_transport_->SetLocalCertificate(certificate);
JsepTransportDescription local_offer =
@ -692,7 +696,7 @@ TEST_F(JsepTransport2Test, InvalidDtlsReofferFromAnswerer) {
rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
rtc::SSLIdentity::Generate("testing", rtc::KT_ECDSA)));
bool rtcp_mux_enabled = true;
CreateJsepTransport2(rtcp_mux_enabled, SrtpMode::kDtlsSrtp);
jsep_transport_ = CreateJsepTransport2(rtcp_mux_enabled, SrtpMode::kDtlsSrtp);
jsep_transport_->SetLocalCertificate(certificate);
JsepTransportDescription local_offer =
@ -738,7 +742,7 @@ TEST_F(JsepTransport2Test, RemoteOfferWithCurrentNegotiatedDtlsRole) {
rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
rtc::SSLIdentity::Generate("testing", rtc::KT_ECDSA)));
bool rtcp_mux_enabled = true;
CreateJsepTransport2(rtcp_mux_enabled, SrtpMode::kDtlsSrtp);
jsep_transport_ = CreateJsepTransport2(rtcp_mux_enabled, SrtpMode::kDtlsSrtp);
jsep_transport_->SetLocalCertificate(certificate);
JsepTransportDescription remote_desc =
@ -783,7 +787,7 @@ TEST_F(JsepTransport2Test, RemoteOfferThatChangesNegotiatedDtlsRole) {
rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
rtc::SSLIdentity::Generate("testing", rtc::KT_ECDSA)));
bool rtcp_mux_enabled = true;
CreateJsepTransport2(rtcp_mux_enabled, SrtpMode::kDtlsSrtp);
jsep_transport_ = CreateJsepTransport2(rtcp_mux_enabled, SrtpMode::kDtlsSrtp);
jsep_transport_->SetLocalCertificate(certificate);
JsepTransportDescription remote_desc =
@ -828,7 +832,7 @@ TEST_F(JsepTransport2Test, DtlsSetupWithLegacyAsAnswerer) {
rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
rtc::SSLIdentity::Generate("testing", rtc::KT_ECDSA)));
bool rtcp_mux_enabled = true;
CreateJsepTransport2(rtcp_mux_enabled, SrtpMode::kDtlsSrtp);
jsep_transport_ = CreateJsepTransport2(rtcp_mux_enabled, SrtpMode::kDtlsSrtp);
jsep_transport_->SetLocalCertificate(certificate);
JsepTransportDescription remote_desc =
@ -860,7 +864,8 @@ TEST_F(JsepTransport2Test, DtlsSetupWithLegacyAsAnswerer) {
// Tests that when the RTCP mux is successfully negotiated, the RTCP transport
// will be destroyed and the SignalRtpMuxActive will be fired.
TEST_F(JsepTransport2Test, RtcpMuxNegotiation) {
CreateJsepTransport2(/*rtcp_mux_enabled=*/false, SrtpMode::kDtlsSrtp);
jsep_transport_ =
CreateJsepTransport2(/*rtcp_mux_enabled=*/false, SrtpMode::kDtlsSrtp);
JsepTransportDescription local_desc;
local_desc.rtcp_mux_enabled = true;
EXPECT_NE(nullptr, jsep_transport_->rtcp_dtls_transport());
@ -882,7 +887,8 @@ TEST_F(JsepTransport2Test, RtcpMuxNegotiation) {
EXPECT_TRUE(signal_rtcp_mux_active_received_);
// The remote side doesn't support RTCP-mux.
CreateJsepTransport2(/*rtcp_mux_enabled=*/false, SrtpMode::kDtlsSrtp);
jsep_transport_ =
CreateJsepTransport2(/*rtcp_mux_enabled=*/false, SrtpMode::kDtlsSrtp);
signal_rtcp_mux_active_received_ = false;
remote_desc.rtcp_mux_enabled = false;
ASSERT_TRUE(
@ -899,9 +905,10 @@ TEST_F(JsepTransport2Test, RtcpMuxNegotiation) {
}
TEST_F(JsepTransport2Test, SdesNegotiation) {
CreateJsepTransport2(/*rtcp_mux_enabled=*/true, SrtpMode::kSdes);
jsep_transport_ =
CreateJsepTransport2(/*rtcp_mux_enabled=*/true, SrtpMode::kSdes);
ASSERT_TRUE(sdes_transport_);
EXPECT_FALSE(sdes_transport_->IsActive());
EXPECT_FALSE(sdes_transport_->IsSrtpActive());
JsepTransportDescription offer_desc;
offer_desc.cryptos.push_back(cricket::CryptoParams(
@ -920,13 +927,14 @@ TEST_F(JsepTransport2Test, SdesNegotiation) {
jsep_transport_
->SetRemoteJsepTransportDescription(answer_desc, SdpType::kAnswer)
.ok());
EXPECT_TRUE(sdes_transport_->IsActive());
EXPECT_TRUE(sdes_transport_->IsSrtpActive());
}
TEST_F(JsepTransport2Test, SdesNegotiationWithEmptyCryptosInAnswer) {
CreateJsepTransport2(/*rtcp_mux_enabled=*/true, SrtpMode::kSdes);
jsep_transport_ =
CreateJsepTransport2(/*rtcp_mux_enabled=*/true, SrtpMode::kSdes);
ASSERT_TRUE(sdes_transport_);
EXPECT_FALSE(sdes_transport_->IsActive());
EXPECT_FALSE(sdes_transport_->IsSrtpActive());
JsepTransportDescription offer_desc;
offer_desc.cryptos.push_back(cricket::CryptoParams(
@ -943,13 +951,14 @@ TEST_F(JsepTransport2Test, SdesNegotiationWithEmptyCryptosInAnswer) {
->SetRemoteJsepTransportDescription(answer_desc, SdpType::kAnswer)
.ok());
// SRTP is not active because the crypto parameter is answer is empty.
EXPECT_FALSE(sdes_transport_->IsActive());
EXPECT_FALSE(sdes_transport_->IsSrtpActive());
}
TEST_F(JsepTransport2Test, SdesNegotiationWithMismatchedCryptos) {
CreateJsepTransport2(/*rtcp_mux_enabled=*/true, SrtpMode::kSdes);
jsep_transport_ =
CreateJsepTransport2(/*rtcp_mux_enabled=*/true, SrtpMode::kSdes);
ASSERT_TRUE(sdes_transport_);
EXPECT_FALSE(sdes_transport_->IsActive());
EXPECT_FALSE(sdes_transport_->IsSrtpActive());
JsepTransportDescription offer_desc;
offer_desc.cryptos.push_back(cricket::CryptoParams(
@ -974,7 +983,8 @@ TEST_F(JsepTransport2Test, SdesNegotiationWithMismatchedCryptos) {
// Tests that the remote candidates can be added to the transports after both
// local and remote descriptions are set.
TEST_F(JsepTransport2Test, AddRemoteCandidates) {
CreateJsepTransport2(/*rtcp_mux_enabled=*/true, SrtpMode::kDtlsSrtp);
jsep_transport_ =
CreateJsepTransport2(/*rtcp_mux_enabled=*/true, SrtpMode::kDtlsSrtp);
auto fake_ice_transport = static_cast<FakeIceTransport*>(
jsep_transport_->rtp_dtls_transport()->ice_transport());
@ -997,4 +1007,237 @@ TEST_F(JsepTransport2Test, AddRemoteCandidates) {
EXPECT_EQ(candidates.size(), fake_ice_transport->remote_candidates().size());
}
enum class Scenario {
kSdes,
kDtlsBeforeCallerSendOffer,
kDtlsBeforeCallerSetAnswer,
kDtlsAfterCallerSetAnswer,
};
class JsepTransport2HeaderExtensionTest
: public JsepTransport2Test,
public ::testing::WithParamInterface<std::tuple<Scenario, bool>> {
protected:
JsepTransport2HeaderExtensionTest() {}
void CreateJsepTransportPair(SrtpMode mode) {
jsep_transport1_ = CreateJsepTransport2(/*rtcp_mux_enabled=*/true, mode);
jsep_transport2_ = CreateJsepTransport2(/*rtcp_mux_enabled=*/true, mode);
auto fake_dtls1 =
static_cast<FakeDtlsTransport*>(jsep_transport1_->rtp_dtls_transport());
auto fake_dtls2 =
static_cast<FakeDtlsTransport*>(jsep_transport2_->rtp_dtls_transport());
fake_dtls1->fake_ice_transport()->SignalReadPacket.connect(
this, &JsepTransport2HeaderExtensionTest::OnReadPacket1);
fake_dtls2->fake_ice_transport()->SignalReadPacket.connect(
this, &JsepTransport2HeaderExtensionTest::OnReadPacket2);
if (mode == SrtpMode::kDtlsSrtp) {
auto cert1 =
rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
rtc::SSLIdentity::Generate("session1", rtc::KT_DEFAULT)));
jsep_transport1_->rtp_dtls_transport()->SetLocalCertificate(cert1);
auto cert2 =
rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
rtc::SSLIdentity::Generate("session1", rtc::KT_DEFAULT)));
jsep_transport2_->rtp_dtls_transport()->SetLocalCertificate(cert2);
}
}
void OnReadPacket1(rtc::PacketTransportInternal* transport,
const char* data,
size_t size,
const rtc::PacketTime& time,
int flags) {
RTC_LOG(LS_INFO) << "JsepTransport 1 Received a packet.";
CompareHeaderExtensions(
reinterpret_cast<const char*>(kPcmuFrameWithExtensions),
sizeof(kPcmuFrameWithExtensions), data, size, recv_encrypted_headers1_,
false);
received_packet_count_++;
}
void OnReadPacket2(rtc::PacketTransportInternal* transport,
const char* data,
size_t size,
const rtc::PacketTime& time,
int flags) {
RTC_LOG(LS_INFO) << "JsepTransport 2 Received a packet.";
CompareHeaderExtensions(
reinterpret_cast<const char*>(kPcmuFrameWithExtensions),
sizeof(kPcmuFrameWithExtensions), data, size, recv_encrypted_headers2_,
false);
received_packet_count_++;
}
void ConnectTransport() {
auto rtp_dtls_transport1 =
static_cast<FakeDtlsTransport*>(jsep_transport1_->rtp_dtls_transport());
auto rtp_dtls_transport2 =
static_cast<FakeDtlsTransport*>(jsep_transport2_->rtp_dtls_transport());
rtp_dtls_transport1->SetDestination(rtp_dtls_transport2);
}
int GetRtpAuthLen() {
bool use_gcm = std::get<1>(GetParam());
if (use_gcm) {
return 16;
}
return 10;
}
void TestSendRecvPacketWithEncryptedHeaderExtension() {
TestOneWaySendRecvPacketWithEncryptedHeaderExtension(
jsep_transport1_.get());
TestOneWaySendRecvPacketWithEncryptedHeaderExtension(
jsep_transport2_.get());
}
void TestOneWaySendRecvPacketWithEncryptedHeaderExtension(
JsepTransport2* sender_transport) {
size_t rtp_len = sizeof(kPcmuFrameWithExtensions);
size_t packet_size = rtp_len + GetRtpAuthLen();
rtc::Buffer rtp_packet_buffer(packet_size);
char* rtp_packet_data = rtp_packet_buffer.data<char>();
memcpy(rtp_packet_data, kPcmuFrameWithExtensions, rtp_len);
// In order to be able to run this test function multiple times we can not
// use the same sequence number twice. Increase the sequence number by one.
rtc::SetBE16(reinterpret_cast<uint8_t*>(rtp_packet_data) + 2,
++sequence_number_);
rtc::CopyOnWriteBuffer rtp_packet(rtp_packet_data, rtp_len, packet_size);
int packet_count_before = received_packet_count_;
rtc::PacketOptions options;
// Send a packet and verify that the packet can be successfully received and
// decrypted.
ASSERT_TRUE(sender_transport->rtp_transport()->SendRtpPacket(
&rtp_packet, options, cricket::PF_SRTP_BYPASS));
EXPECT_EQ(packet_count_before + 1, received_packet_count_);
}
int sequence_number_ = 0;
int received_packet_count_ = 0;
std::unique_ptr<JsepTransport2> jsep_transport1_;
std::unique_ptr<JsepTransport2> jsep_transport2_;
std::vector<int> recv_encrypted_headers1_;
std::vector<int> recv_encrypted_headers2_;
};
// Test that the encrypted header extension works and can be changed in
// different scenarios.
TEST_P(JsepTransport2HeaderExtensionTest, EncryptedHeaderExtensionNegotiation) {
Scenario scenario = std::get<0>(GetParam());
bool use_gcm = std::get<1>(GetParam());
SrtpMode mode = SrtpMode ::kDtlsSrtp;
if (scenario == Scenario::kSdes) {
mode = SrtpMode::kSdes;
}
CreateJsepTransportPair(mode);
recv_encrypted_headers1_.push_back(kHeaderExtensionIDs[0]);
recv_encrypted_headers2_.push_back(kHeaderExtensionIDs[1]);
cricket::CryptoParams sdes_param(1, rtc::CS_AES_CM_128_HMAC_SHA1_80,
"inline:" + rtc::CreateRandomString(40),
std::string());
if (use_gcm) {
auto fake_dtls1 =
static_cast<FakeDtlsTransport*>(jsep_transport1_->rtp_dtls_transport());
auto fake_dtls2 =
static_cast<FakeDtlsTransport*>(jsep_transport2_->rtp_dtls_transport());
fake_dtls1->SetSrtpCryptoSuite(rtc::SRTP_AEAD_AES_256_GCM);
fake_dtls2->SetSrtpCryptoSuite(rtc::SRTP_AEAD_AES_256_GCM);
}
if (scenario == Scenario::kDtlsBeforeCallerSendOffer) {
ConnectTransport();
}
JsepTransportDescription offer_desc;
offer_desc.encrypted_header_extension_ids = recv_encrypted_headers1_;
if (scenario == Scenario::kSdes) {
offer_desc.cryptos.push_back(sdes_param);
}
ASSERT_TRUE(
jsep_transport1_
->SetLocalJsepTransportDescription(offer_desc, SdpType::kOffer)
.ok());
ASSERT_TRUE(
jsep_transport2_
->SetRemoteJsepTransportDescription(offer_desc, SdpType::kOffer)
.ok());
JsepTransportDescription answer_desc;
answer_desc.encrypted_header_extension_ids = recv_encrypted_headers2_;
if (scenario == Scenario::kSdes) {
answer_desc.cryptos.push_back(sdes_param);
}
ASSERT_TRUE(
jsep_transport2_
->SetLocalJsepTransportDescription(answer_desc, SdpType::kAnswer)
.ok());
if (scenario == Scenario::kDtlsBeforeCallerSetAnswer) {
ConnectTransport();
// Sending packet from transport2 to transport1 should work when they are
// partially configured.
TestOneWaySendRecvPacketWithEncryptedHeaderExtension(
/*sender_transport=*/jsep_transport2_.get());
}
ASSERT_TRUE(
jsep_transport1_
->SetRemoteJsepTransportDescription(answer_desc, SdpType::kAnswer)
.ok());
if (scenario == Scenario::kDtlsAfterCallerSetAnswer ||
scenario == Scenario::kSdes) {
ConnectTransport();
}
EXPECT_TRUE(jsep_transport1_->rtp_transport()->IsSrtpActive());
EXPECT_TRUE(jsep_transport2_->rtp_transport()->IsSrtpActive());
TestSendRecvPacketWithEncryptedHeaderExtension();
// Change the encrypted header extension in a new offer/answer exchange.
recv_encrypted_headers1_.clear();
recv_encrypted_headers2_.clear();
recv_encrypted_headers1_.push_back(kHeaderExtensionIDs[1]);
recv_encrypted_headers2_.push_back(kHeaderExtensionIDs[0]);
offer_desc.encrypted_header_extension_ids = recv_encrypted_headers1_;
answer_desc.encrypted_header_extension_ids = recv_encrypted_headers2_;
ASSERT_TRUE(
jsep_transport1_
->SetLocalJsepTransportDescription(offer_desc, SdpType::kOffer)
.ok());
ASSERT_TRUE(
jsep_transport2_
->SetRemoteJsepTransportDescription(offer_desc, SdpType::kOffer)
.ok());
ASSERT_TRUE(
jsep_transport2_
->SetLocalJsepTransportDescription(answer_desc, SdpType::kAnswer)
.ok());
ASSERT_TRUE(
jsep_transport1_
->SetRemoteJsepTransportDescription(answer_desc, SdpType::kAnswer)
.ok());
EXPECT_TRUE(jsep_transport1_->rtp_transport()->IsSrtpActive());
EXPECT_TRUE(jsep_transport2_->rtp_transport()->IsSrtpActive());
TestSendRecvPacketWithEncryptedHeaderExtension();
}
INSTANTIATE_TEST_CASE_P(
JsepTransport2Test,
JsepTransport2HeaderExtensionTest,
::testing::Values(
std::make_tuple(Scenario::kSdes, false),
std::make_tuple(Scenario::kDtlsBeforeCallerSendOffer, true),
std::make_tuple(Scenario::kDtlsBeforeCallerSetAnswer, true),
std::make_tuple(Scenario::kDtlsAfterCallerSetAnswer, true),
std::make_tuple(Scenario::kDtlsBeforeCallerSendOffer, false),
std::make_tuple(Scenario::kDtlsBeforeCallerSetAnswer, false),
std::make_tuple(Scenario::kDtlsAfterCallerSetAnswer, false)));
} // namespace cricket

View File

@ -1,673 +0,0 @@
/*
* Copyright 2011 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/jseptransport.h"
#include <memory>
#include "p2p/base/fakedtlstransport.h"
#include "p2p/base/fakeicetransport.h"
#include "rtc_base/fakesslidentity.h"
#include "rtc_base/gunit.h"
#include "rtc_base/network.h"
namespace cricket {
using rtc::SocketAddress;
using webrtc::SdpType;
static const char kIceUfrag1[] = "TESTICEUFRAG0001";
static const char kIcePwd1[] = "TESTICEPWD00000000000001";
static const char kIceUfrag2[] = "TESTICEUFRAG0002";
static const char kIcePwd2[] = "TESTICEPWD00000000000002";
TransportDescription MakeTransportDescription(
const char* ufrag,
const char* pwd,
const rtc::scoped_refptr<rtc::RTCCertificate>& cert,
ConnectionRole role = CONNECTIONROLE_NONE) {
std::unique_ptr<rtc::SSLFingerprint> fingerprint;
if (cert) {
fingerprint.reset(rtc::SSLFingerprint::CreateFromCertificate(cert));
}
return TransportDescription(std::vector<std::string>(), ufrag, pwd,
ICEMODE_FULL, role, fingerprint.get());
}
class JsepTransportTest : public testing::Test, public sigslot::has_slots<> {
public:
JsepTransportTest() { RecreateTransport(); }
bool SetupFakeTransports(int component) {
fake_ice_transports_.emplace_back(
new FakeIceTransport(transport_->mid(), component));
fake_dtls_transports_.emplace_back(
new FakeDtlsTransport(fake_ice_transports_.back().get()));
return transport_->AddChannel(fake_dtls_transports_.back().get(),
component);
}
void DestroyChannel(int component) { transport_->RemoveChannel(component); }
void RecreateTransport() {
transport_.reset(new JsepTransport("test content name", nullptr));
}
bool IceCredentialsChanged(const std::string& old_ufrag,
const std::string& old_pwd,
const std::string& new_ufrag,
const std::string& new_pwd) {
return (old_ufrag != new_ufrag) || (old_pwd != new_pwd);
}
protected:
std::vector<std::unique_ptr<FakeDtlsTransport>> fake_dtls_transports_;
std::vector<std::unique_ptr<FakeIceTransport>> fake_ice_transports_;
std::unique_ptr<JsepTransport> transport_;
};
// This test verifies transports are created with proper ICE ufrag/password
// after a transport description is applied.
TEST_F(JsepTransportTest, TestIceTransportParameters) {
EXPECT_TRUE(SetupFakeTransports(1));
TransportDescription local_desc(kIceUfrag1, kIcePwd1);
ASSERT_TRUE(transport_->SetLocalTransportDescription(local_desc,
SdpType::kOffer, NULL));
EXPECT_EQ(ICEMODE_FULL, fake_ice_transports_[0]->remote_ice_mode());
EXPECT_EQ(kIceUfrag1, fake_ice_transports_[0]->ice_ufrag());
EXPECT_EQ(kIcePwd1, fake_ice_transports_[0]->ice_pwd());
TransportDescription remote_desc(kIceUfrag2, kIcePwd2);
ASSERT_TRUE(transport_->SetRemoteTransportDescription(
remote_desc, SdpType::kAnswer, NULL));
EXPECT_EQ(ICEMODE_FULL, fake_ice_transports_[0]->remote_ice_mode());
EXPECT_EQ(kIceUfrag2, fake_ice_transports_[0]->remote_ice_ufrag());
EXPECT_EQ(kIcePwd2, fake_ice_transports_[0]->remote_ice_pwd());
}
// Similarly, test that DTLS parameters are applied after a transport
// description is applied.
TEST_F(JsepTransportTest, TestDtlsTransportParameters) {
EXPECT_TRUE(SetupFakeTransports(1));
// Create certificates.
rtc::scoped_refptr<rtc::RTCCertificate> local_cert =
rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
rtc::SSLIdentity::Generate("local", rtc::KT_DEFAULT)));
rtc::scoped_refptr<rtc::RTCCertificate> remote_cert =
rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
rtc::SSLIdentity::Generate("remote", rtc::KT_DEFAULT)));
transport_->SetLocalCertificate(local_cert);
// Apply offer/answer.
TransportDescription local_desc = MakeTransportDescription(
kIceUfrag1, kIcePwd1, local_cert, CONNECTIONROLE_ACTPASS);
ASSERT_TRUE(transport_->SetLocalTransportDescription(
local_desc, SdpType::kOffer, nullptr));
TransportDescription remote_desc = MakeTransportDescription(
kIceUfrag2, kIcePwd2, remote_cert, CONNECTIONROLE_ACTIVE);
ASSERT_TRUE(transport_->SetRemoteTransportDescription(
remote_desc, SdpType::kAnswer, nullptr));
// Verify that SSL role and remote fingerprint were set correctly based on
// transport descriptions.
rtc::SSLRole role;
EXPECT_TRUE(fake_dtls_transports_[0]->GetDtlsRole(&role));
EXPECT_EQ(rtc::SSL_SERVER, role); // Because remote description was "active".
EXPECT_EQ(remote_desc.identity_fingerprint->ToString(),
fake_dtls_transports_[0]->dtls_fingerprint().ToString());
}
// Same as above test, but with remote transport description using
// CONNECTIONROLE_PASSIVE, expecting SSL_CLIENT role.
TEST_F(JsepTransportTest, TestDtlsTransportParametersWithPassiveAnswer) {
EXPECT_TRUE(SetupFakeTransports(1));
// Create certificates.
rtc::scoped_refptr<rtc::RTCCertificate> local_cert =
rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
rtc::SSLIdentity::Generate("local", rtc::KT_DEFAULT)));
rtc::scoped_refptr<rtc::RTCCertificate> remote_cert =
rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
rtc::SSLIdentity::Generate("remote", rtc::KT_DEFAULT)));
transport_->SetLocalCertificate(local_cert);
// Apply offer/answer.
TransportDescription local_desc = MakeTransportDescription(
kIceUfrag1, kIcePwd1, local_cert, CONNECTIONROLE_ACTPASS);
ASSERT_TRUE(transport_->SetLocalTransportDescription(
local_desc, SdpType::kOffer, nullptr));
TransportDescription remote_desc = MakeTransportDescription(
kIceUfrag2, kIcePwd2, remote_cert, CONNECTIONROLE_PASSIVE);
ASSERT_TRUE(transport_->SetRemoteTransportDescription(
remote_desc, SdpType::kAnswer, nullptr));
// Verify that SSL role and remote fingerprint were set correctly based on
// transport descriptions.
rtc::SSLRole role;
EXPECT_TRUE(fake_dtls_transports_[0]->GetDtlsRole(&role));
EXPECT_EQ(rtc::SSL_CLIENT,
role); // Because remote description was "passive".
EXPECT_EQ(remote_desc.identity_fingerprint->ToString(),
fake_dtls_transports_[0]->dtls_fingerprint().ToString());
}
// Add two DtlsTransports/IceTransports and make sure parameters are applied to
// both of them. Applicable when RTP/RTCP are not multiplexed, so they share
// the same parameters but different connections.
TEST_F(JsepTransportTest, TestTransportParametersAppliedToTwoComponents) {
EXPECT_TRUE(SetupFakeTransports(1));
EXPECT_TRUE(SetupFakeTransports(2));
// Create certificates.
rtc::scoped_refptr<rtc::RTCCertificate> local_cert =
rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
rtc::SSLIdentity::Generate("local", rtc::KT_DEFAULT)));
rtc::scoped_refptr<rtc::RTCCertificate> remote_cert =
rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
rtc::SSLIdentity::Generate("remote", rtc::KT_DEFAULT)));
transport_->SetLocalCertificate(local_cert);
// Apply offer/answer.
TransportDescription local_desc = MakeTransportDescription(
kIceUfrag1, kIcePwd1, local_cert, CONNECTIONROLE_ACTPASS);
ASSERT_TRUE(transport_->SetLocalTransportDescription(
local_desc, SdpType::kOffer, nullptr));
TransportDescription remote_desc = MakeTransportDescription(
kIceUfrag2, kIcePwd2, remote_cert, CONNECTIONROLE_ACTIVE);
ASSERT_TRUE(transport_->SetRemoteTransportDescription(
remote_desc, SdpType::kAnswer, nullptr));
for (int i = 0; i < 1; ++i) {
// Verify parameters of ICE transports.
EXPECT_EQ(ICEMODE_FULL, fake_ice_transports_[i]->remote_ice_mode());
EXPECT_EQ(kIceUfrag1, fake_ice_transports_[i]->ice_ufrag());
EXPECT_EQ(kIcePwd1, fake_ice_transports_[i]->ice_pwd());
EXPECT_EQ(kIceUfrag2, fake_ice_transports_[i]->remote_ice_ufrag());
EXPECT_EQ(kIcePwd2, fake_ice_transports_[i]->remote_ice_pwd());
// Verify parameters of DTLS transports.
rtc::SSLRole role;
EXPECT_TRUE(fake_dtls_transports_[i]->GetDtlsRole(&role));
EXPECT_EQ(rtc::SSL_SERVER,
role); // Because remote description was "active".
EXPECT_EQ(remote_desc.identity_fingerprint->ToString(),
fake_dtls_transports_[i]->dtls_fingerprint().ToString());
}
}
// Verifies that IceCredentialsChanged returns true when either ufrag or pwd
// changed, and false in other cases.
TEST_F(JsepTransportTest, TestIceCredentialsChanged) {
EXPECT_TRUE(IceCredentialsChanged("u1", "p1", "u2", "p2"));
EXPECT_TRUE(IceCredentialsChanged("u1", "p1", "u2", "p1"));
EXPECT_TRUE(IceCredentialsChanged("u1", "p1", "u1", "p2"));
EXPECT_FALSE(IceCredentialsChanged("u1", "p1", "u1", "p1"));
}
// Tests SetNeedsIceRestartFlag and NeedsIceRestart, ensuring NeedsIceRestart
// only starts returning "false" once an ICE restart has been initiated.
TEST_F(JsepTransportTest, NeedsIceRestart) {
// Do initial offer/answer so there's something to restart.
TransportDescription local_desc(kIceUfrag1, kIcePwd1);
TransportDescription remote_desc(kIceUfrag1, kIcePwd1);
ASSERT_TRUE(transport_->SetLocalTransportDescription(
local_desc, SdpType::kOffer, nullptr));
ASSERT_TRUE(transport_->SetRemoteTransportDescription(
remote_desc, SdpType::kAnswer, nullptr));
// Flag initially should be false.
EXPECT_FALSE(transport_->NeedsIceRestart());
// After setting flag, it should be true.
transport_->SetNeedsIceRestartFlag();
EXPECT_TRUE(transport_->NeedsIceRestart());
// Doing an identical offer/answer shouldn't clear the flag.
ASSERT_TRUE(transport_->SetLocalTransportDescription(
local_desc, SdpType::kOffer, nullptr));
ASSERT_TRUE(transport_->SetRemoteTransportDescription(
remote_desc, SdpType::kAnswer, nullptr));
EXPECT_TRUE(transport_->NeedsIceRestart());
// Doing an offer/answer that restarts ICE should clear the flag.
TransportDescription ice_restart_local_desc(kIceUfrag2, kIcePwd2);
TransportDescription ice_restart_remote_desc(kIceUfrag2, kIcePwd2);
ASSERT_TRUE(transport_->SetLocalTransportDescription(
ice_restart_local_desc, SdpType::kOffer, nullptr));
ASSERT_TRUE(transport_->SetRemoteTransportDescription(
ice_restart_remote_desc, SdpType::kAnswer, nullptr));
EXPECT_FALSE(transport_->NeedsIceRestart());
}
TEST_F(JsepTransportTest, TestGetStats) {
EXPECT_TRUE(SetupFakeTransports(1));
TransportStats stats;
EXPECT_TRUE(transport_->GetStats(&stats));
// Note that this tests the behavior of a FakeIceTransport.
ASSERT_EQ(1U, stats.channel_stats.size());
EXPECT_EQ(1, stats.channel_stats[0].component);
// Set local transport description for FakeTransport before connecting.
TransportDescription faketransport_desc(
std::vector<std::string>(), rtc::CreateRandomString(ICE_UFRAG_LENGTH),
rtc::CreateRandomString(ICE_PWD_LENGTH), ICEMODE_FULL,
CONNECTIONROLE_NONE, nullptr);
transport_->SetLocalTransportDescription(faketransport_desc, SdpType::kOffer,
nullptr);
EXPECT_TRUE(transport_->GetStats(&stats));
ASSERT_EQ(1U, stats.channel_stats.size());
EXPECT_EQ(1, stats.channel_stats[0].component);
}
// Tests that VerifyCertificateFingerprint only returns true when the
// certificate matches the fingerprint.
TEST_F(JsepTransportTest, TestVerifyCertificateFingerprint) {
std::string error_desc;
EXPECT_FALSE(
transport_->VerifyCertificateFingerprint(nullptr, nullptr, &error_desc));
rtc::KeyType key_types[] = {rtc::KT_RSA, rtc::KT_ECDSA};
for (auto& key_type : key_types) {
rtc::scoped_refptr<rtc::RTCCertificate> certificate =
rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
rtc::SSLIdentity::Generate("testing", key_type)));
ASSERT_NE(nullptr, certificate);
std::string digest_algorithm;
ASSERT_TRUE(certificate->ssl_certificate().GetSignatureDigestAlgorithm(
&digest_algorithm));
ASSERT_FALSE(digest_algorithm.empty());
std::unique_ptr<rtc::SSLFingerprint> good_fingerprint(
rtc::SSLFingerprint::Create(digest_algorithm, certificate->identity()));
ASSERT_NE(nullptr, good_fingerprint);
EXPECT_TRUE(transport_->VerifyCertificateFingerprint(
certificate.get(), good_fingerprint.get(), &error_desc));
EXPECT_FALSE(transport_->VerifyCertificateFingerprint(
certificate.get(), nullptr, &error_desc));
EXPECT_FALSE(transport_->VerifyCertificateFingerprint(
nullptr, good_fingerprint.get(), &error_desc));
rtc::SSLFingerprint bad_fingerprint = *good_fingerprint;
bad_fingerprint.digest.AppendData("0", 1);
EXPECT_FALSE(transport_->VerifyCertificateFingerprint(
certificate.get(), &bad_fingerprint, &error_desc));
}
}
// Tests the logic of DTLS role negotiation for an initial offer/answer.
TEST_F(JsepTransportTest, DtlsRoleNegotiation) {
// Just use the same certificate for both sides; doesn't really matter in a
// non end-to-end test.
rtc::scoped_refptr<rtc::RTCCertificate> certificate =
rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
rtc::SSLIdentity::Generate("testing", rtc::KT_ECDSA)));
TransportDescription local_desc =
MakeTransportDescription(kIceUfrag1, kIcePwd1, certificate);
TransportDescription remote_desc =
MakeTransportDescription(kIceUfrag2, kIcePwd2, certificate);
struct NegotiateRoleParams {
ConnectionRole local_role;
ConnectionRole remote_role;
SdpType local_type;
SdpType remote_type;
};
std::string error_desc;
// Parameters which set the SSL role to SSL_CLIENT.
NegotiateRoleParams valid_client_params[] = {
{CONNECTIONROLE_ACTIVE, CONNECTIONROLE_ACTPASS, SdpType::kAnswer,
SdpType::kOffer},
{CONNECTIONROLE_ACTIVE, CONNECTIONROLE_ACTPASS, SdpType::kPrAnswer,
SdpType::kOffer},
{CONNECTIONROLE_ACTPASS, CONNECTIONROLE_PASSIVE, SdpType::kOffer,
SdpType::kAnswer},
{CONNECTIONROLE_ACTPASS, CONNECTIONROLE_PASSIVE, SdpType::kOffer,
SdpType::kPrAnswer}};
for (auto& param : valid_client_params) {
RecreateTransport();
transport_->SetLocalCertificate(certificate);
local_desc.connection_role = param.local_role;
remote_desc.connection_role = param.remote_role;
// Set the offer first.
if (param.local_type == SdpType::kOffer) {
EXPECT_TRUE(transport_->SetLocalTransportDescription(
local_desc, param.local_type, nullptr));
EXPECT_TRUE(transport_->SetRemoteTransportDescription(
remote_desc, param.remote_type, nullptr));
} else {
EXPECT_TRUE(transport_->SetRemoteTransportDescription(
remote_desc, param.remote_type, nullptr));
EXPECT_TRUE(transport_->SetLocalTransportDescription(
local_desc, param.local_type, nullptr));
}
EXPECT_EQ(rtc::SSL_CLIENT, *transport_->GetSslRole());
}
// Parameters which set the SSL role to SSL_SERVER.
NegotiateRoleParams valid_server_params[] = {
{CONNECTIONROLE_PASSIVE, CONNECTIONROLE_ACTPASS, SdpType::kAnswer,
SdpType::kOffer},
{CONNECTIONROLE_PASSIVE, CONNECTIONROLE_ACTPASS, SdpType::kPrAnswer,
SdpType::kOffer},
{CONNECTIONROLE_ACTPASS, CONNECTIONROLE_ACTIVE, SdpType::kOffer,
SdpType::kAnswer},
{CONNECTIONROLE_ACTPASS, CONNECTIONROLE_ACTIVE, SdpType::kOffer,
SdpType::kPrAnswer}};
for (auto& param : valid_server_params) {
RecreateTransport();
transport_->SetLocalCertificate(certificate);
local_desc.connection_role = param.local_role;
remote_desc.connection_role = param.remote_role;
// Set the offer first.
if (param.local_type == SdpType::kOffer) {
EXPECT_TRUE(transport_->SetLocalTransportDescription(
local_desc, param.local_type, nullptr));
EXPECT_TRUE(transport_->SetRemoteTransportDescription(
remote_desc, param.remote_type, nullptr));
} else {
EXPECT_TRUE(transport_->SetRemoteTransportDescription(
remote_desc, param.remote_type, nullptr));
EXPECT_TRUE(transport_->SetLocalTransportDescription(
local_desc, param.local_type, nullptr));
}
EXPECT_EQ(rtc::SSL_SERVER, *transport_->GetSslRole());
}
// Invalid parameters due to both peers having a duplicate role.
NegotiateRoleParams duplicate_params[] = {
{CONNECTIONROLE_ACTIVE, CONNECTIONROLE_ACTIVE, SdpType::kAnswer,
SdpType::kOffer},
{CONNECTIONROLE_ACTPASS, CONNECTIONROLE_ACTPASS, SdpType::kAnswer,
SdpType::kOffer},
{CONNECTIONROLE_PASSIVE, CONNECTIONROLE_PASSIVE, SdpType::kAnswer,
SdpType::kOffer},
{CONNECTIONROLE_ACTIVE, CONNECTIONROLE_ACTIVE, SdpType::kPrAnswer,
SdpType::kOffer},
{CONNECTIONROLE_ACTPASS, CONNECTIONROLE_ACTPASS, SdpType::kPrAnswer,
SdpType::kOffer},
{CONNECTIONROLE_PASSIVE, CONNECTIONROLE_PASSIVE, SdpType::kPrAnswer,
SdpType::kOffer},
{CONNECTIONROLE_ACTIVE, CONNECTIONROLE_ACTIVE, SdpType::kOffer,
SdpType::kAnswer},
{CONNECTIONROLE_ACTPASS, CONNECTIONROLE_ACTPASS, SdpType::kOffer,
SdpType::kAnswer},
{CONNECTIONROLE_PASSIVE, CONNECTIONROLE_PASSIVE, SdpType::kOffer,
SdpType::kAnswer},
{CONNECTIONROLE_ACTIVE, CONNECTIONROLE_ACTIVE, SdpType::kOffer,
SdpType::kPrAnswer},
{CONNECTIONROLE_ACTPASS, CONNECTIONROLE_ACTPASS, SdpType::kOffer,
SdpType::kPrAnswer},
{CONNECTIONROLE_PASSIVE, CONNECTIONROLE_PASSIVE, SdpType::kOffer,
SdpType::kPrAnswer}};
for (auto& param : duplicate_params) {
RecreateTransport();
transport_->SetLocalCertificate(certificate);
local_desc.connection_role = param.local_role;
remote_desc.connection_role = param.remote_role;
// Set the offer first.
if (param.local_type == SdpType::kOffer) {
EXPECT_TRUE(transport_->SetLocalTransportDescription(
local_desc, param.local_type, nullptr));
EXPECT_FALSE(transport_->SetRemoteTransportDescription(
remote_desc, param.remote_type, nullptr));
} else {
EXPECT_TRUE(transport_->SetRemoteTransportDescription(
remote_desc, param.remote_type, nullptr));
EXPECT_FALSE(transport_->SetLocalTransportDescription(
local_desc, param.local_type, nullptr));
}
}
// Invalid parameters due to the offerer not using ACTPASS.
NegotiateRoleParams offerer_without_actpass_params[] = {
{CONNECTIONROLE_ACTIVE, CONNECTIONROLE_PASSIVE, SdpType::kAnswer,
SdpType::kOffer},
{CONNECTIONROLE_PASSIVE, CONNECTIONROLE_ACTIVE, SdpType::kAnswer,
SdpType::kOffer},
{CONNECTIONROLE_ACTPASS, CONNECTIONROLE_PASSIVE, SdpType::kAnswer,
SdpType::kOffer},
{CONNECTIONROLE_ACTIVE, CONNECTIONROLE_PASSIVE, SdpType::kPrAnswer,
SdpType::kOffer},
{CONNECTIONROLE_PASSIVE, CONNECTIONROLE_ACTIVE, SdpType::kPrAnswer,
SdpType::kOffer},
{CONNECTIONROLE_ACTPASS, CONNECTIONROLE_PASSIVE, SdpType::kPrAnswer,
SdpType::kOffer},
{CONNECTIONROLE_ACTIVE, CONNECTIONROLE_PASSIVE, SdpType::kOffer,
SdpType::kAnswer},
{CONNECTIONROLE_PASSIVE, CONNECTIONROLE_ACTIVE, SdpType::kOffer,
SdpType::kAnswer},
{CONNECTIONROLE_PASSIVE, CONNECTIONROLE_ACTPASS, SdpType::kOffer,
SdpType::kAnswer},
{CONNECTIONROLE_ACTIVE, CONNECTIONROLE_PASSIVE, SdpType::kOffer,
SdpType::kPrAnswer},
{CONNECTIONROLE_PASSIVE, CONNECTIONROLE_ACTIVE, SdpType::kOffer,
SdpType::kPrAnswer},
{CONNECTIONROLE_PASSIVE, CONNECTIONROLE_ACTPASS, SdpType::kOffer,
SdpType::kPrAnswer}};
for (auto& param : offerer_without_actpass_params) {
RecreateTransport();
transport_->SetLocalCertificate(certificate);
local_desc.connection_role = param.local_role;
remote_desc.connection_role = param.remote_role;
// Set the offer first.
// TODO(deadbeef): Really this should fail as soon as the offer is
// attempted to be applied, and not when the answer is applied.
if (param.local_type == SdpType::kOffer) {
EXPECT_TRUE(transport_->SetLocalTransportDescription(
local_desc, param.local_type, nullptr));
EXPECT_FALSE(transport_->SetRemoteTransportDescription(
remote_desc, param.remote_type, nullptr));
} else {
EXPECT_TRUE(transport_->SetRemoteTransportDescription(
remote_desc, param.remote_type, nullptr));
EXPECT_FALSE(transport_->SetLocalTransportDescription(
local_desc, param.local_type, nullptr));
}
}
}
// Test that a reoffer in the opposite direction is successful as long as the
// role isn't changing. Doesn't test every possible combination like the test
// above.
TEST_F(JsepTransportTest, ValidDtlsReofferFromAnswerer) {
// Just use the same certificate for both sides; doesn't really matter in a
// non end-to-end test.
rtc::scoped_refptr<rtc::RTCCertificate> certificate =
rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
rtc::SSLIdentity::Generate("testing", rtc::KT_ECDSA)));
transport_->SetLocalCertificate(certificate);
TransportDescription local_offer = MakeTransportDescription(
kIceUfrag1, kIcePwd1, certificate, CONNECTIONROLE_ACTPASS);
TransportDescription remote_answer = MakeTransportDescription(
kIceUfrag2, kIcePwd2, certificate, CONNECTIONROLE_ACTIVE);
EXPECT_TRUE(transport_->SetLocalTransportDescription(
local_offer, SdpType::kOffer, nullptr));
EXPECT_TRUE(transport_->SetRemoteTransportDescription(
remote_answer, SdpType::kAnswer, nullptr));
// We were actpass->active previously, now in the other direction it's
// actpass->passive.
TransportDescription remote_offer = MakeTransportDescription(
kIceUfrag2, kIcePwd2, certificate, CONNECTIONROLE_ACTPASS);
TransportDescription local_answer = MakeTransportDescription(
kIceUfrag1, kIcePwd1, certificate, CONNECTIONROLE_PASSIVE);
EXPECT_TRUE(transport_->SetRemoteTransportDescription(
remote_offer, SdpType::kOffer, nullptr));
EXPECT_TRUE(transport_->SetLocalTransportDescription(
local_answer, SdpType::kAnswer, nullptr));
}
// Test that a reoffer in the opposite direction fails if the role changes.
// Inverse of test above.
TEST_F(JsepTransportTest, InvalidDtlsReofferFromAnswerer) {
// Just use the same certificate for both sides; doesn't really matter in a
// non end-to-end test.
rtc::scoped_refptr<rtc::RTCCertificate> certificate =
rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
rtc::SSLIdentity::Generate("testing", rtc::KT_ECDSA)));
transport_->SetLocalCertificate(certificate);
TransportDescription local_offer = MakeTransportDescription(
kIceUfrag1, kIcePwd1, certificate, CONNECTIONROLE_ACTPASS);
TransportDescription remote_answer = MakeTransportDescription(
kIceUfrag2, kIcePwd2, certificate, CONNECTIONROLE_ACTIVE);
EXPECT_TRUE(transport_->SetLocalTransportDescription(
local_offer, SdpType::kOffer, nullptr));
EXPECT_TRUE(transport_->SetRemoteTransportDescription(
remote_answer, SdpType::kAnswer, nullptr));
// Changing role to passive here isn't allowed. Though for some reason this
// only fails in SetLocalTransportDescription.
TransportDescription remote_offer = MakeTransportDescription(
kIceUfrag2, kIcePwd2, certificate, CONNECTIONROLE_PASSIVE);
TransportDescription local_answer = MakeTransportDescription(
kIceUfrag1, kIcePwd1, certificate, CONNECTIONROLE_ACTIVE);
EXPECT_TRUE(transport_->SetRemoteTransportDescription(
remote_offer, SdpType::kOffer, nullptr));
EXPECT_FALSE(transport_->SetLocalTransportDescription(
local_answer, SdpType::kAnswer, nullptr));
}
// Test that a remote offer with the current negotiated role can be accepted.
// This is allowed by dtls-sdp, though we'll never generate such an offer,
// since JSEP requires generating "actpass".
TEST_F(JsepTransportTest, RemoteOfferWithCurrentNegotiatedDtlsRole) {
// Just use the same certificate in both descriptions; the remote fingerprint
// doesn't matter in a non end-to-end test.
rtc::scoped_refptr<rtc::RTCCertificate> certificate =
rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
rtc::SSLIdentity::Generate("testing", rtc::KT_ECDSA)));
transport_->SetLocalCertificate(certificate);
TransportDescription remote_desc = MakeTransportDescription(
kIceUfrag1, kIcePwd1, certificate, CONNECTIONROLE_ACTPASS);
TransportDescription local_desc = MakeTransportDescription(
kIceUfrag2, kIcePwd2, certificate, CONNECTIONROLE_ACTIVE);
// Normal initial offer/answer with "actpass" in the offer and "active" in
// the answer.
ASSERT_TRUE(transport_->SetRemoteTransportDescription(
remote_desc, SdpType::kOffer, nullptr));
ASSERT_TRUE(transport_->SetLocalTransportDescription(
local_desc, SdpType::kAnswer, nullptr));
// Sanity check that role was actually negotiated.
rtc::Optional<rtc::SSLRole> role = transport_->GetSslRole();
ASSERT_TRUE(role);
EXPECT_EQ(rtc::SSL_CLIENT, *role);
// Subsequent offer with current negotiated role of "passive".
remote_desc.connection_role = CONNECTIONROLE_PASSIVE;
EXPECT_TRUE(transport_->SetRemoteTransportDescription(
remote_desc, SdpType::kOffer, nullptr));
EXPECT_TRUE(transport_->SetLocalTransportDescription(
local_desc, SdpType::kAnswer, nullptr));
}
// Test that a remote offer with the inverse of the current negotiated DTLS
// role is rejected.
TEST_F(JsepTransportTest, RemoteOfferThatChangesNegotiatedDtlsRole) {
rtc::scoped_refptr<rtc::RTCCertificate> certificate =
rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
rtc::SSLIdentity::Generate("testing", rtc::KT_ECDSA)));
std::unique_ptr<rtc::SSLFingerprint> fingerprint(
rtc::SSLFingerprint::CreateFromCertificate(certificate));
transport_->SetLocalCertificate(certificate);
TransportDescription local_desc(kIceUfrag1, kIcePwd1);
TransportDescription remote_desc(kIceUfrag2, kIcePwd2);
// Just use the same fingerprint in both descriptions; the remote fingerprint
// doesn't matter in a non end-to-end test.
local_desc.identity_fingerprint.reset(
TransportDescription::CopyFingerprint(fingerprint.get()));
remote_desc.identity_fingerprint.reset(
TransportDescription::CopyFingerprint(fingerprint.get()));
remote_desc.connection_role = CONNECTIONROLE_ACTPASS;
local_desc.connection_role = CONNECTIONROLE_ACTIVE;
// Normal initial offer/answer with "actpass" in the offer and "active" in
// the answer.
ASSERT_TRUE(transport_->SetRemoteTransportDescription(
remote_desc, SdpType::kOffer, nullptr));
ASSERT_TRUE(transport_->SetLocalTransportDescription(
local_desc, SdpType::kAnswer, nullptr));
// Sanity check that role was actually negotiated.
rtc::Optional<rtc::SSLRole> role = transport_->GetSslRole();
ASSERT_TRUE(role);
EXPECT_EQ(rtc::SSL_CLIENT, *role);
// Subsequent offer with "active", which is the opposite of the remote
// endpoint's negotiated role.
// TODO(deadbeef): Really this should fail as soon as the offer is
// attempted to be applied, and not when the answer is applied.
remote_desc.connection_role = CONNECTIONROLE_ACTIVE;
EXPECT_TRUE(transport_->SetRemoteTransportDescription(
remote_desc, SdpType::kOffer, nullptr));
EXPECT_FALSE(transport_->SetLocalTransportDescription(
local_desc, SdpType::kAnswer, nullptr));
}
// Testing that a legacy client that doesn't use the setup attribute will be
// interpreted as having an active role.
TEST_F(JsepTransportTest, TestDtlsSetupWithLegacyAsAnswerer) {
rtc::scoped_refptr<rtc::RTCCertificate> certificate =
rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
rtc::SSLIdentity::Generate("testing", rtc::KT_ECDSA)));
std::unique_ptr<rtc::SSLFingerprint> fingerprint(
rtc::SSLFingerprint::CreateFromCertificate(certificate));
transport_->SetLocalCertificate(certificate);
TransportDescription local_desc(kIceUfrag1, kIcePwd1);
TransportDescription remote_desc(kIceUfrag2, kIcePwd2);
// Just use the same fingerprint in both descriptions; the remote fingerprint
// doesn't matter in a non end-to-end test.
local_desc.identity_fingerprint.reset(
TransportDescription::CopyFingerprint(fingerprint.get()));
remote_desc.identity_fingerprint.reset(
TransportDescription::CopyFingerprint(fingerprint.get()));
local_desc.connection_role = CONNECTIONROLE_ACTPASS;
ASSERT_TRUE(transport_->SetLocalTransportDescription(
local_desc, SdpType::kOffer, nullptr));
// Use CONNECTIONROLE_NONE to simulate legacy endpoint.
remote_desc.connection_role = CONNECTIONROLE_NONE;
ASSERT_TRUE(transport_->SetRemoteTransportDescription(
remote_desc, SdpType::kAnswer, nullptr));
rtc::Optional<rtc::SSLRole> role = transport_->GetSslRole();
ASSERT_TRUE(role);
// Since legacy answer ommitted setup atribute, and we offered actpass, we
// should act as passive (server).
EXPECT_EQ(rtc::SSL_SERVER, *role);
}
} // namespace cricket

View File

@ -136,7 +136,7 @@ RTCError JsepTransportController::SetRemoteDescription(
RtpTransportInternal* JsepTransportController::GetRtpTransport(
const std::string& mid) const {
auto jsep_transport = GetJsepTransport(mid);
auto jsep_transport = GetJsepTransportForMid(mid);
if (!jsep_transport) {
return nullptr;
}
@ -145,7 +145,7 @@ RtpTransportInternal* JsepTransportController::GetRtpTransport(
cricket::DtlsTransportInternal* JsepTransportController::GetDtlsTransport(
const std::string& mid) const {
auto jsep_transport = GetJsepTransport(mid);
auto jsep_transport = GetJsepTransportForMid(mid);
if (!jsep_transport) {
return nullptr;
}
@ -154,7 +154,7 @@ cricket::DtlsTransportInternal* JsepTransportController::GetDtlsTransport(
cricket::DtlsTransportInternal* JsepTransportController::GetRtcpDtlsTransport(
const std::string& mid) const {
auto jsep_transport = GetJsepTransport(mid);
auto jsep_transport = GetJsepTransportForMid(mid);
if (!jsep_transport) {
return nullptr;
}
@ -174,14 +174,15 @@ void JsepTransportController::SetIceConfig(const cricket::IceConfig& config) {
}
void JsepTransportController::SetNeedsIceRestartFlag() {
for (auto& kv : jsep_transports_by_mid_) {
for (auto& kv : jsep_transports_by_name_) {
kv.second->SetNeedsIceRestartFlag();
}
}
bool JsepTransportController::NeedsIceRestart(
const std::string& transport_name) const {
const cricket::JsepTransport2* transport = GetJsepTransport(transport_name);
const cricket::JsepTransport2* transport =
GetJsepTransportByName(transport_name);
if (!transport) {
return false;
}
@ -189,13 +190,13 @@ bool JsepTransportController::NeedsIceRestart(
}
rtc::Optional<rtc::SSLRole> JsepTransportController::GetDtlsRole(
const std::string& transport_name) const {
const std::string& mid) const {
if (!network_thread_->IsCurrent()) {
return network_thread_->Invoke<rtc::Optional<rtc::SSLRole>>(
RTC_FROM_HERE, [&] { return GetDtlsRole(transport_name); });
RTC_FROM_HERE, [&] { return GetDtlsRole(mid); });
}
const cricket::JsepTransport2* t = GetJsepTransport(transport_name);
const cricket::JsepTransport2* t = GetJsepTransportForMid(mid);
if (!t) {
return rtc::Optional<rtc::SSLRole>();
}
@ -218,7 +219,7 @@ bool JsepTransportController::SetLocalCertificate(
// Set certificate for JsepTransport, which verifies it matches the
// fingerprint in SDP, and DTLS transport.
// Fallback from DTLS to SDES is not supported.
for (auto& kv : jsep_transports_by_mid_) {
for (auto& kv : jsep_transports_by_name_) {
kv.second->SetLocalCertificate(certificate_);
}
for (auto& dtls : GetDtlsTransports()) {
@ -236,7 +237,7 @@ JsepTransportController::GetLocalCertificate(
RTC_FROM_HERE, [&] { return GetLocalCertificate(transport_name); });
}
const cricket::JsepTransport2* t = GetJsepTransport(transport_name);
const cricket::JsepTransport2* t = GetJsepTransportByName(transport_name);
if (!t) {
return nullptr;
}
@ -251,10 +252,14 @@ JsepTransportController::GetRemoteSSLCertChain(
RTC_FROM_HERE, [&] { return GetRemoteSSLCertChain(transport_name); });
}
// Get the certificate from the RTP channel's DTLS handshake. Should be
// identical to the RTCP channel's, since they were given the same remote
// Get the certificate from the RTP transport's DTLS handshake. Should be
// identical to the RTCP transport's, since they were given the same remote
// fingerprint.
auto dtls = GetDtlsTransport(transport_name);
auto jsep_transport = GetJsepTransportByName(transport_name);
if (!jsep_transport) {
return nullptr;
}
auto dtls = jsep_transport->rtp_dtls_transport();
if (!dtls) {
return nullptr;
}
@ -288,13 +293,12 @@ RTCError JsepTransportController::AddRemoteCandidates(
if (!error.ok()) {
return error;
}
cricket::JsepTransport2* jsep_transport = GetJsepTransport(transport_name);
auto jsep_transport = GetJsepTransportByName(transport_name);
if (!jsep_transport) {
return RTCError(RTCErrorType::INVALID_PARAMETER,
"The transport doesn't exist.");
RTC_LOG(LS_WARNING) << "Not adding candidate because the JsepTransport "
"doesn't exist. Ignore it.";
return RTCError::OK();
}
return jsep_transport->AddRemoteCandidates(candidates);
}
@ -325,10 +329,12 @@ RTCError JsepTransportController::RemoveRemoteCandidates(
for (const auto& kv : candidates_by_transport_name) {
const std::string& transport_name = kv.first;
const cricket::Candidates& candidates = kv.second;
cricket::JsepTransport2* jsep_transport = GetJsepTransport(transport_name);
cricket::JsepTransport2* jsep_transport =
GetJsepTransportByName(transport_name);
if (!jsep_transport) {
return RTCError(RTCErrorType::INVALID_PARAMETER,
"The transport doesn't exist.");
RTC_LOG(LS_WARNING)
<< "Not removing candidate because the JsepTransport doesn't exist.";
continue;
}
for (const cricket::Candidate& candidate : candidates) {
auto dtls = candidate.component() == cricket::ICE_CANDIDATE_COMPONENT_RTP
@ -349,7 +355,7 @@ bool JsepTransportController::GetStats(const std::string& transport_name,
RTC_FROM_HERE, [=] { return GetStats(transport_name, stats); });
}
cricket::JsepTransport2* transport = GetJsepTransport(transport_name);
cricket::JsepTransport2* transport = GetJsepTransportByName(transport_name);
if (!transport) {
return false;
}
@ -427,23 +433,27 @@ JsepTransportController::CreateUnencryptedRtpTransport(
rtc::PacketTransportInternal* rtp_packet_transport,
rtc::PacketTransportInternal* rtcp_packet_transport) {
RTC_DCHECK(network_thread_->IsCurrent());
// TODO(zhihuang): Add support of unencrypted RTP for testing.
return nullptr;
auto unencrypted_rtp_transport =
rtc::MakeUnique<RtpTransport>(rtcp_packet_transport == nullptr);
unencrypted_rtp_transport->SetRtpPacketTransport(rtp_packet_transport);
if (rtcp_packet_transport) {
unencrypted_rtp_transport->SetRtcpPacketTransport(rtcp_packet_transport);
}
return unencrypted_rtp_transport;
}
std::unique_ptr<webrtc::SrtpTransport>
JsepTransportController::CreateSdesTransport(
const std::string& transport_name,
rtc::PacketTransportInternal* rtp_packet_transport,
rtc::PacketTransportInternal* rtcp_packet_transport) {
cricket::DtlsTransportInternal* rtp_dtls_transport,
cricket::DtlsTransportInternal* rtcp_dtls_transport) {
RTC_DCHECK(network_thread_->IsCurrent());
bool rtcp_mux_enabled = rtcp_packet_transport == nullptr;
auto srtp_transport =
rtc::MakeUnique<webrtc::SrtpTransport>(rtcp_mux_enabled);
RTC_DCHECK(rtp_packet_transport);
srtp_transport->SetRtpPacketTransport(rtp_packet_transport);
if (rtcp_packet_transport) {
srtp_transport->SetRtcpPacketTransport(rtp_packet_transport);
rtc::MakeUnique<webrtc::SrtpTransport>(rtcp_dtls_transport == nullptr);
RTC_DCHECK(rtp_dtls_transport);
srtp_transport->SetRtpPacketTransport(rtp_dtls_transport);
if (rtcp_dtls_transport) {
srtp_transport->SetRtcpPacketTransport(rtcp_dtls_transport);
}
if (config_.enable_external_auth) {
srtp_transport->EnableExternalAuth();
@ -457,9 +467,8 @@ JsepTransportController::CreateDtlsSrtpTransport(
cricket::DtlsTransportInternal* rtp_dtls_transport,
cricket::DtlsTransportInternal* rtcp_dtls_transport) {
RTC_DCHECK(network_thread_->IsCurrent());
bool rtcp_mux_enabled = rtcp_dtls_transport == nullptr;
auto srtp_transport =
rtc::MakeUnique<webrtc::SrtpTransport>(rtcp_mux_enabled);
rtc::MakeUnique<webrtc::SrtpTransport>(rtcp_dtls_transport == nullptr);
if (config_.enable_external_auth) {
srtp_transport->EnableExternalAuth();
}
@ -475,8 +484,8 @@ JsepTransportController::CreateDtlsSrtpTransport(
std::vector<cricket::DtlsTransportInternal*>
JsepTransportController::GetDtlsTransports() {
std::vector<cricket::DtlsTransportInternal*> dtls_transports;
for (auto it = jsep_transports_by_mid_.begin();
it != jsep_transports_by_mid_.end(); ++it) {
for (auto it = jsep_transports_by_name_.begin();
it != jsep_transports_by_name_.end(); ++it) {
auto jsep_transport = it->second.get();
RTC_DCHECK(jsep_transport);
if (jsep_transport->rtp_dtls_transport()) {
@ -535,7 +544,18 @@ RTCError JsepTransportController::ApplyDescription_n(
}
if (ShouldUpdateBundleGroup(type, description)) {
bundle_group_ = *description->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
if (!description->HasGroup(cricket::GROUP_TYPE_BUNDLE)) {
return RTCError(RTCErrorType::INVALID_PARAMETER,
"max-bundle is used but no bundle group found.");
} else {
bundle_group_ = *description->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
}
}
RTCError error;
error = ValidateBundleGroup(description);
if (!error.ok()) {
return error;
}
std::vector<int> merged_encrypted_extension_ids;
@ -550,7 +570,10 @@ RTCError JsepTransportController::ApplyDescription_n(
(IsBundled(content_info.name) && content_info.name != *bundled_mid())) {
continue;
}
MaybeCreateJsepTransport(content_info.name, content_info);
error = MaybeCreateJsepTransport(content_info.name, content_info);
if (!error.ok()) {
return error;
}
}
RTC_DCHECK(description->contents().size() ==
@ -569,6 +592,11 @@ RTCError JsepTransportController::ApplyDescription_n(
continue;
}
error = ValidateContent(content_info);
if (!error.ok()) {
return error;
}
std::vector<int> extension_ids;
if (bundle_group_ && content_info.name == *bundled_mid()) {
extension_ids = merged_encrypted_extension_ids;
@ -576,15 +604,18 @@ RTCError JsepTransportController::ApplyDescription_n(
extension_ids = GetEncryptedHeaderExtensionIds(content_info);
}
cricket::JsepTransport2* transport = GetJsepTransport(content_info.name);
int rtp_abs_sendtime_extn_id =
GetRtpAbsSendTimeHeaderExtensionId(content_info);
cricket::JsepTransport2* transport =
GetJsepTransportForMid(content_info.name);
RTC_DCHECK(transport);
SetIceRole_n(DetermineIceRole(transport, transport_info, type, local));
RTCError error;
cricket::JsepTransportDescription jsep_description =
CreateJsepTransportDescription(content_info, transport_info,
extension_ids);
extension_ids, rtp_abs_sendtime_extn_id);
if (local) {
error =
transport->SetLocalJsepTransportDescription(jsep_description, type);
@ -602,6 +633,51 @@ RTCError JsepTransportController::ApplyDescription_n(
return RTCError::OK();
}
RTCError JsepTransportController::ValidateBundleGroup(
const cricket::SessionDescription* description) {
RTC_DCHECK(description);
if (!bundled_mid()) {
return RTCError::OK();
}
auto bundled_content = description->GetContentByName(*bundled_mid());
if (!bundled_content) {
return RTCError(
RTCErrorType::INVALID_PARAMETER,
"An m= section associated with the BUNDLE-tag doesn't exist.");
}
// If the |bundled_content| is rejected, other contents in the bundle group
// should be rejected.
if (bundled_content->rejected) {
for (auto content_name : bundle_group_->content_names()) {
auto other_content = description->GetContentByName(content_name);
if (!other_content->rejected) {
return RTCError(
RTCErrorType::INVALID_PARAMETER,
"The m= section:" + content_name + " should be rejected.");
}
}
}
return RTCError::OK();
}
RTCError JsepTransportController::ValidateContent(
const cricket::ContentInfo& content_info) {
if (config_.rtcp_mux_policy ==
PeerConnectionInterface::kRtcpMuxPolicyRequire &&
content_info.type == cricket::MediaProtocolType::kRtp &&
!content_info.media_description()->rtcp_mux()) {
return RTCError(RTCErrorType::INVALID_PARAMETER,
"The m= section:" + content_info.name +
" is invalid. RTCP-MUX is not "
"enabled when it is required.");
}
return RTCError::OK();
}
void JsepTransportController::HandleRejectedContent(
const cricket::ContentInfo& content_info) {
// If the content is rejected, let the
@ -612,9 +688,24 @@ void JsepTransportController::HandleRejectedContent(
} else {
SignalDtlsTransportChanged(content_info.name, nullptr);
}
// Remove the rejected content from the |bundle_group_|.
if (IsBundled(content_info.name)) {
// If the answerer rejects the first content, which other contents are bundled
// on, all the other contents in the bundle group will be rejected.
if (content_info.name == bundled_mid()) {
for (auto content_name : bundle_group_->content_names()) {
if (content_info.type == cricket::MediaProtocolType::kRtp) {
SignalRtpTransportChanged(content_name, nullptr);
} else {
SignalDtlsTransportChanged(content_name, nullptr);
}
}
bundle_group_.reset();
} else if (IsBundled(content_info.name)) {
// Remove the rejected content from the |bundle_group_|.
bundle_group_->RemoveContentName(content_info.name);
// Reset the bundle group if nothing left.
if (!bundle_group_->FirstContentName()) {
bundle_group_.reset();
}
}
MaybeDestroyJsepTransport(content_info.name);
}
@ -626,11 +717,11 @@ void JsepTransportController::HandleBundledContent(
// then destroy the cricket::JsepTransport2.
if (content_info.type == cricket::MediaProtocolType::kRtp) {
auto rtp_transport =
jsep_transports_by_mid_[*bundled_mid()]->rtp_transport();
jsep_transports_by_name_[*bundled_mid()]->rtp_transport();
SignalRtpTransportChanged(content_info.name, rtp_transport);
} else {
auto dtls_transport =
jsep_transports_by_mid_[*bundled_mid()]->rtp_dtls_transport();
jsep_transports_by_name_[*bundled_mid()]->rtp_dtls_transport();
SignalDtlsTransportChanged(content_info.name, dtls_transport);
}
MaybeDestroyJsepTransport(content_info.name);
@ -640,7 +731,8 @@ cricket::JsepTransportDescription
JsepTransportController::CreateJsepTransportDescription(
cricket::ContentInfo content_info,
cricket::TransportInfo transport_info,
const std::vector<int>& encrypted_extension_ids) {
const std::vector<int>& encrypted_extension_ids,
int rtp_abs_sendtime_extn_id) {
const cricket::MediaContentDescription* content_desc =
static_cast<const cricket::MediaContentDescription*>(
content_info.description);
@ -651,7 +743,7 @@ JsepTransportController::CreateJsepTransportDescription(
return cricket::JsepTransportDescription(
rtcp_mux_enabled, content_desc->cryptos(), encrypted_extension_ids,
transport_info.description);
rtp_abs_sendtime_extn_id, transport_info.description);
}
bool JsepTransportController::ShouldUpdateBundleGroup(
@ -721,47 +813,79 @@ JsepTransportController::MergeEncryptedHeaderExtensionIdsForBundle(
return merged_ids;
}
const cricket::JsepTransport2* JsepTransportController::GetJsepTransport(
const std::string& mid) const {
auto target_mid = mid;
if (IsBundled(mid)) {
target_mid = *bundled_mid();
}
auto it = jsep_transports_by_mid_.find(target_mid);
return (it == jsep_transports_by_mid_.end()) ? nullptr : it->second.get();
}
cricket::JsepTransport2* JsepTransportController::GetJsepTransport(
const std::string& mid) {
auto target_mid = mid;
if (IsBundled(mid)) {
target_mid = *bundled_mid();
}
auto it = jsep_transports_by_mid_.find(target_mid);
return (it == jsep_transports_by_mid_.end()) ? nullptr : it->second.get();
}
void JsepTransportController::MaybeCreateJsepTransport(
const std::string& mid,
int JsepTransportController::GetRtpAbsSendTimeHeaderExtensionId(
const cricket::ContentInfo& content_info) {
RTC_DCHECK(network_thread_->IsCurrent());
cricket::JsepTransport2* transport = GetJsepTransport(mid);
if (transport) {
return;
if (!config_.enable_external_auth) {
return -1;
}
const cricket::MediaContentDescription* content_desc =
static_cast<const cricket::MediaContentDescription*>(
content_info.description);
bool rtcp_mux_enabled =
content_desc->rtcp_mux() ||
config_.rtcp_mux_policy == PeerConnectionInterface::kRtcpMuxPolicyRequire;
const webrtc::RtpExtension* send_time_extension =
webrtc::RtpExtension::FindHeaderExtensionByUri(
content_desc->rtp_header_extensions(),
webrtc::RtpExtension::kAbsSendTimeUri);
return send_time_extension ? send_time_extension->id : -1;
}
const cricket::JsepTransport2* JsepTransportController::GetJsepTransportForMid(
const std::string& mid) const {
auto target_mid = mid;
if (IsBundled(mid)) {
target_mid = *bundled_mid();
}
auto it = jsep_transports_by_name_.find(target_mid);
return (it == jsep_transports_by_name_.end()) ? nullptr : it->second.get();
}
cricket::JsepTransport2* JsepTransportController::GetJsepTransportForMid(
const std::string& mid) {
auto target_mid = mid;
if (IsBundled(mid)) {
target_mid = *bundled_mid();
}
auto it = jsep_transports_by_name_.find(target_mid);
return (it == jsep_transports_by_name_.end()) ? nullptr : it->second.get();
}
const cricket::JsepTransport2* JsepTransportController::GetJsepTransportByName(
const std::string& transport_name) const {
auto it = jsep_transports_by_name_.find(transport_name);
return (it == jsep_transports_by_name_.end()) ? nullptr : it->second.get();
}
cricket::JsepTransport2* JsepTransportController::GetJsepTransportByName(
const std::string& transport_name) {
auto it = jsep_transports_by_name_.find(transport_name);
return (it == jsep_transports_by_name_.end()) ? nullptr : it->second.get();
}
RTCError JsepTransportController::MaybeCreateJsepTransport(
const std::string& mid,
const cricket::ContentInfo& content_info) {
RTC_DCHECK(network_thread_->IsCurrent());
cricket::JsepTransport2* transport = GetJsepTransportForMid(mid);
if (transport) {
return RTCError::OK();
}
const cricket::MediaContentDescription* content_desc =
static_cast<const cricket::MediaContentDescription*>(
content_info.description);
if (certificate_ && !content_desc->cryptos().empty()) {
return RTCError(RTCErrorType::INVALID_PARAMETER,
"SDES and DTLS-SRTP cannot be enabled at the same time.");
}
std::unique_ptr<cricket::DtlsTransportInternal> rtp_dtls_transport =
CreateDtlsTransport(mid, /*rtcp = */ false);
CreateDtlsTransport(mid, /*rtcp =*/false);
std::unique_ptr<cricket::DtlsTransportInternal> rtcp_dtls_transport;
if (!rtcp_mux_enabled) {
rtcp_dtls_transport = CreateDtlsTransport(mid, /*rtcp = */ true);
if (config_.rtcp_mux_policy !=
PeerConnectionInterface::kRtcpMuxPolicyRequire &&
content_info.type == cricket::MediaProtocolType::kRtp) {
rtcp_dtls_transport = CreateDtlsTransport(mid, /*rtcp =*/true);
}
std::unique_ptr<RtpTransport> unencrypted_rtp_transport;
@ -785,19 +909,21 @@ void JsepTransportController::MaybeCreateJsepTransport(
std::move(rtp_dtls_transport), std::move(rtcp_dtls_transport));
jsep_transport->SignalRtcpMuxActive.connect(
this, &JsepTransportController::UpdateAggregateStates_n);
jsep_transports_by_mid_[mid] = std::move(jsep_transport);
jsep_transports_by_name_[mid] = std::move(jsep_transport);
UpdateAggregateStates_n();
return RTCError::OK();
}
void JsepTransportController::MaybeDestroyJsepTransport(
const std::string& mid) {
jsep_transports_by_mid_.erase(mid);
jsep_transports_by_name_.erase(mid);
UpdateAggregateStates_n();
}
void JsepTransportController::DestroyAllJsepTransports_n() {
RTC_DCHECK(network_thread_->IsCurrent());
jsep_transports_by_mid_.clear();
jsep_transports_by_name_.clear();
}
void JsepTransportController::SetIceRole_n(cricket::IceRole ice_role) {
@ -871,7 +997,7 @@ cricket::IceRole JsepTransportController::DetermineIceRole(
jsep_transport->local_description()->transport_desc.ice_mode ==
cricket::ICEMODE_LITE &&
ice_role_ == cricket::ICEROLE_CONTROLLING &&
tdesc.ice_mode == cricket::ICEMODE_LITE) {
tdesc.ice_mode == cricket::ICEMODE_FULL) {
ice_role = cricket::ICEROLE_CONTROLLED;
}
}

View File

@ -135,6 +135,7 @@ class JsepTransportController : public sigslot::has_slots<>,
bool GetStats(const std::string& mid, cricket::TransportStats* stats);
void SetMetricsObserver(webrtc::MetricsObserverInterface* metrics_observer);
bool initial_offerer() const { return initial_offerer_ && *initial_offerer_; }
// All of these signals are fired on the signaling thread.
// If any transport failed => failed,
@ -176,6 +177,8 @@ class JsepTransportController : public sigslot::has_slots<>,
RTCError ApplyDescription_n(bool local,
SdpType type,
const cricket::SessionDescription* description);
RTCError ValidateBundleGroup(const cricket::SessionDescription* description);
RTCError ValidateContent(const cricket::ContentInfo& content_info);
void HandleRejectedContent(const cricket::ContentInfo& content_info);
void HandleBundledContent(const cricket::ContentInfo& content_info);
@ -183,7 +186,8 @@ class JsepTransportController : public sigslot::has_slots<>,
cricket::JsepTransportDescription CreateJsepTransportDescription(
cricket::ContentInfo content_info,
cricket::TransportInfo transport_info,
const std::vector<int>& encrypted_extension_ids);
const std::vector<int>& encrypted_extension_ids,
int rtp_abs_sendtime_extn_id);
rtc::Optional<std::string> bundled_mid() const {
rtc::Optional<std::string> bundled_mid;
@ -202,15 +206,29 @@ class JsepTransportController : public sigslot::has_slots<>,
std::vector<int> MergeEncryptedHeaderExtensionIdsForBundle(
const cricket::SessionDescription* description);
std::vector<int> GetEncryptedHeaderExtensionIds(
const cricket::ContentInfo& content_info);
const cricket::JsepTransport2* GetJsepTransport(const std::string& mid) const;
cricket::JsepTransport2* GetJsepTransport(const std::string& mid);
int GetRtpAbsSendTimeHeaderExtensionId(
const cricket::ContentInfo& content_info);
void MaybeCreateJsepTransport(const std::string& mid,
const cricket::ContentInfo& content_info);
// This method takes the BUNDLE group into account. If the JsepTransport is
// destroyed because of BUNDLE, it would return the transport which other
// transports are bundled on (In current implementation, it is the first
// content in the BUNDLE group).
const cricket::JsepTransport2* GetJsepTransportForMid(
const std::string& mid) const;
cricket::JsepTransport2* GetJsepTransportForMid(const std::string& mid);
// Get the JsepTransport without considering the BUNDLE group. Return nullptr
// if the JsepTransport is destroyed.
const cricket::JsepTransport2* GetJsepTransportByName(
const std::string& transport_name) const;
cricket::JsepTransport2* GetJsepTransportByName(
const std::string& transport_name);
RTCError MaybeCreateJsepTransport(const std::string& mid,
const cricket::ContentInfo& content_info);
void MaybeDestroyJsepTransport(const std::string& mid);
void DestroyAllJsepTransports_n();
@ -232,8 +250,8 @@ class JsepTransportController : public sigslot::has_slots<>,
rtc::PacketTransportInternal* rtcp_packet_transport);
std::unique_ptr<webrtc::SrtpTransport> CreateSdesTransport(
const std::string& transport_name,
rtc::PacketTransportInternal* rtp_packet_transport,
rtc::PacketTransportInternal* rtcp_packet_transport);
cricket::DtlsTransportInternal* rtp_dtls_transport,
cricket::DtlsTransportInternal* rtcp_dtls_transport);
std::unique_ptr<webrtc::DtlsSrtpTransport> CreateDtlsSrtpTransport(
const std::string& transport_name,
cricket::DtlsTransportInternal* rtp_dtls_transport,
@ -265,9 +283,9 @@ class JsepTransportController : public sigslot::has_slots<>,
cricket::PortAllocator* const port_allocator_ = nullptr;
std::map<std::string, std::unique_ptr<cricket::JsepTransport2>>
jsep_transports_by_mid_;
jsep_transports_by_name_;
// Aggregate state for TransportChannelImpls.
// Aggregate state for Transports.
cricket::IceConnectionState ice_connection_state_ =
cricket::kIceConnectionConnecting;
cricket::IceGatheringState ice_gathering_state_ = cricket::kIceGatheringNew;

View File

@ -123,6 +123,8 @@ class JsepTransportControllerTest : public testing::Test,
rtc::scoped_refptr<rtc::RTCCertificate> cert) {
std::unique_ptr<cricket::AudioContentDescription> audio(
new cricket::AudioContentDescription());
// Set RTCP-mux to be true because the default policy is "mux required".
audio->set_rtcp_mux(true);
description->AddContent(mid, cricket::MediaProtocolType::kRtp,
/*rejected=*/false, audio.release());
AddTransportInfo(description, mid, ufrag, pwd, ice_mode, conn_role, cert);
@ -137,6 +139,8 @@ class JsepTransportControllerTest : public testing::Test,
rtc::scoped_refptr<rtc::RTCCertificate> cert) {
std::unique_ptr<cricket::VideoContentDescription> video(
new cricket::VideoContentDescription());
// Set RTCP-mux to be true because the default policy is "mux required".
video->set_rtcp_mux(true);
description->AddContent(mid, cricket::MediaProtocolType::kRtp,
/*rejected=*/false, video.release());
AddTransportInfo(description, mid, ufrag, pwd, ice_mode, conn_role, cert);
@ -152,6 +156,7 @@ class JsepTransportControllerTest : public testing::Test,
rtc::scoped_refptr<rtc::RTCCertificate> cert) {
std::unique_ptr<cricket::DataContentDescription> data(
new cricket::DataContentDescription());
data->set_rtcp_mux(true);
description->AddContent(mid, protocol_type,
/*rejected=*/false, data.release());
AddTransportInfo(description, mid, ufrag, pwd, ice_mode, conn_role, cert);
@ -325,6 +330,20 @@ TEST_F(JsepTransportControllerTest, GetDtlsTransport) {
EXPECT_EQ(nullptr, transport_controller_->GetRtcpDtlsTransport(kVideoMid2));
}
TEST_F(JsepTransportControllerTest, GetDtlsTransportWithRtcpMux) {
JsepTransportController::Config config;
config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyRequire;
CreateJsepTransportController(config);
auto description = CreateSessionDescriptionWithoutBundle();
EXPECT_TRUE(transport_controller_
->SetLocalDescription(SdpType::kOffer, description.get())
.ok());
EXPECT_NE(nullptr, transport_controller_->GetDtlsTransport(kAudioMid1));
EXPECT_EQ(nullptr, transport_controller_->GetRtcpDtlsTransport(kAudioMid1));
EXPECT_NE(nullptr, transport_controller_->GetDtlsTransport(kVideoMid1));
EXPECT_EQ(nullptr, transport_controller_->GetRtcpDtlsTransport(kVideoMid1));
}
TEST_F(JsepTransportControllerTest, SetIceConfig) {
CreateJsepTransportController(JsepTransportController::Config());
auto description = CreateSessionDescriptionWithoutBundle();
@ -1207,5 +1226,103 @@ TEST_F(JsepTransportControllerTest, ChangeBundledMidNotSupported) {
->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
.ok());
}
// Test that rejecting only the first m= section of a BUNDLE group is treated as
// an error, but rejecting all of them works as expected.
TEST_F(JsepTransportControllerTest, RejectFirstContentInBundleGroup) {
CreateJsepTransportController(JsepTransportController::Config());
cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
bundle_group.AddContentName(kAudioMid1);
bundle_group.AddContentName(kVideoMid1);
bundle_group.AddContentName(kDataMid1);
auto local_offer = rtc::MakeUnique<cricket::SessionDescription>();
AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
nullptr);
AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
nullptr);
AddDataSection(local_offer.get(), kDataMid1,
cricket::MediaProtocolType::kSctp, kIceUfrag3, kIcePwd3,
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
nullptr);
auto remote_answer = rtc::MakeUnique<cricket::SessionDescription>();
AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
nullptr);
AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
nullptr);
AddDataSection(remote_answer.get(), kDataMid1,
cricket::MediaProtocolType::kSctp, kIceUfrag3, kIcePwd3,
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
nullptr);
// Reject audio content in answer.
remote_answer->contents()[0].rejected = true;
local_offer->AddGroup(bundle_group);
remote_answer->AddGroup(bundle_group);
EXPECT_TRUE(transport_controller_
->SetLocalDescription(SdpType::kOffer, local_offer.get())
.ok());
EXPECT_FALSE(transport_controller_
->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
.ok());
// Reject all the contents.
remote_answer->contents()[1].rejected = true;
remote_answer->contents()[2].rejected = true;
EXPECT_TRUE(transport_controller_
->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
.ok());
EXPECT_EQ(nullptr, transport_controller_->GetRtpTransport(kAudioMid1));
EXPECT_EQ(nullptr, transport_controller_->GetRtpTransport(kVideoMid1));
EXPECT_EQ(nullptr, transport_controller_->GetDtlsTransport(kDataMid1));
}
// Tests that applying non-RTCP-mux offer would fail when kRtcpMuxPolicyRequire
// is used.
TEST_F(JsepTransportControllerTest, ApplyNonRtcpMuxOfferWhenMuxingRequired) {
JsepTransportController::Config config;
config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyRequire;
CreateJsepTransportController(config);
auto local_offer = rtc::MakeUnique<cricket::SessionDescription>();
AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
nullptr);
local_offer->contents()[0].media_description()->set_rtcp_mux(false);
// Applying a non-RTCP-mux offer is expected to fail.
EXPECT_FALSE(transport_controller_
->SetLocalDescription(SdpType::kOffer, local_offer.get())
.ok());
}
// Tests that applying non-RTCP-mux answer would fail when kRtcpMuxPolicyRequire
// is used.
TEST_F(JsepTransportControllerTest, ApplyNonRtcpMuxAnswerWhenMuxingRequired) {
JsepTransportController::Config config;
config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyRequire;
CreateJsepTransportController(config);
auto local_offer = rtc::MakeUnique<cricket::SessionDescription>();
AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
nullptr);
EXPECT_TRUE(transport_controller_
->SetLocalDescription(SdpType::kOffer, local_offer.get())
.ok());
auto remote_answer = rtc::MakeUnique<cricket::SessionDescription>();
AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
nullptr);
// Applying a non-RTCP-mux answer is expected to fail.
remote_answer->contents()[0].media_description()->set_rtcp_mux(false);
EXPECT_FALSE(transport_controller_
->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
.ok());
}
} // namespace webrtc

View File

@ -22,7 +22,7 @@
#include "media/base/mediaconstants.h"
#include "media/base/mediaengine.h" // For DataChannelType
#include "p2p/base/transportdescriptionfactory.h"
#include "pc/jseptransport.h"
#include "pc/jseptransport2.h"
#include "pc/sessiondescription.h"
namespace cricket {

View File

@ -89,7 +89,6 @@ const char kDtlsSrtpSetupFailureRtp[] =
"Couldn't set up DTLS-SRTP on RTP channel.";
const char kDtlsSrtpSetupFailureRtcp[] =
"Couldn't set up DTLS-SRTP on RTCP channel.";
const char kEnableBundleFailed[] = "Failed to enable BUNDLE.";
namespace {
@ -907,25 +906,40 @@ bool PeerConnection::Initialize(
return false;
}
const PeerConnectionFactoryInterface::Options& options = factory_->options();
// RFC 3264: The numeric value of the session id and version in the
// o line MUST be representable with a "64 bit signed integer".
// Due to this constraint session id |session_id_| is max limited to
// LLONG_MAX.
session_id_ = rtc::ToString(rtc::CreateRandomId64() & LLONG_MAX);
transport_controller_.reset(factory_->CreateTransportController(
port_allocator_.get(), configuration.redetermine_role_on_ice_restart,
event_log_.get()));
transport_controller_->SetIceRole(cricket::ICEROLE_CONTROLLED);
transport_controller_->SignalConnectionState.connect(
JsepTransportController::Config config;
config.redetermine_role_on_ice_restart =
configuration.redetermine_role_on_ice_restart;
config.ssl_max_version = factory_->options().ssl_max_version;
config.disable_encryption = options.disable_encryption;
config.bundle_policy = configuration.bundle_policy;
config.rtcp_mux_policy = configuration.rtcp_mux_policy;
config.crypto_options = options.crypto_options;
#if defined(ENABLE_EXTERNAL_AUTH)
config.enable_external_auth = true;
#endif
transport_controller_.reset(new JsepTransportController(
signaling_thread(), network_thread(), port_allocator_.get(), config));
transport_controller_->SignalIceConnectionState.connect(
this, &PeerConnection::OnTransportControllerConnectionState);
transport_controller_->SignalGatheringState.connect(
transport_controller_->SignalIceGatheringState.connect(
this, &PeerConnection::OnTransportControllerGatheringState);
transport_controller_->SignalCandidatesGathered.connect(
transport_controller_->SignalIceCandidatesGathered.connect(
this, &PeerConnection::OnTransportControllerCandidatesGathered);
transport_controller_->SignalCandidatesRemoved.connect(
transport_controller_->SignalIceCandidatesRemoved.connect(
this, &PeerConnection::OnTransportControllerCandidatesRemoved);
transport_controller_->SignalDtlsHandshakeError.connect(
this, &PeerConnection::OnTransportControllerDtlsHandshakeError);
transport_controller_->SignalRtpTransportChanged.connect(
this, &PeerConnection::OnRtpTransportChanged);
transport_controller_->SignalDtlsTransportChanged.connect(
this, &PeerConnection::OnDtlsTransportChanged);
sctp_factory_ = factory_->CreateSctpTransportInternalFactory();
@ -934,10 +948,6 @@ bool PeerConnection::Initialize(
configuration_ = configuration;
const PeerConnectionFactoryInterface::Options& options = factory_->options();
transport_controller_->SetSslMaxProtocolVersion(options.ssl_max_version);
// Obtain a certificate from RTCConfiguration if any were provided (optional).
rtc::scoped_refptr<rtc::RTCCertificate> certificate;
if (!configuration.certificates.empty()) {
@ -1975,17 +1985,6 @@ RTCError PeerConnection::ApplyLocalDescription(
// streams that might be removed by updating the session description.
stats_->UpdateStats(kStatsOutputLevelStandard);
// Update the initial_offerer flag if this session is the initial_offerer.
SdpType type = desc->GetType();
if (!initial_offerer_.has_value()) {
initial_offerer_.emplace(type == SdpType::kOffer);
if (*initial_offerer_) {
transport_controller_->SetIceRole(cricket::ICEROLE_CONTROLLING);
} else {
transport_controller_->SetIceRole(cricket::ICEROLE_CONTROLLED);
}
}
// Take a reference to the old local description since it's used below to
// compare against the new local description. When setting the new local
// description, grab ownership of the replaced session description in case it
@ -1994,6 +1993,7 @@ RTCError PeerConnection::ApplyLocalDescription(
const SessionDescriptionInterface* old_local_description =
local_description();
std::unique_ptr<SessionDescriptionInterface> replaced_local_description;
SdpType type = desc->GetType();
if (type == SdpType::kAnswer) {
replaced_local_description = pending_local_description_
? std::move(pending_local_description_)
@ -2009,6 +2009,11 @@ RTCError PeerConnection::ApplyLocalDescription(
// |local_description()|.
RTC_DCHECK(local_description());
RTCError error = PushdownTransportDescription(cricket::CS_LOCAL, type);
if (!error.ok()) {
return error;
}
if (IsUnifiedPlan()) {
RTCError error = UpdateTransceiversAndDataChannels(
cricket::CS_LOCAL, *local_description(), old_local_description,
@ -2031,7 +2036,8 @@ RTCError PeerConnection::ApplyLocalDescription(
}
}
} else {
// Transport and Media channels will be created only when offer is set.
// Media channels will be created only when offer is set. These may use new
// transports just created by PushdownTransportDescription.
if (type == SdpType::kOffer) {
// TODO(bugs.webrtc.org/4676) - Handle CreateChannel failure, as new local
// description is applied. Restore back to old description.
@ -2040,12 +2046,12 @@ RTCError PeerConnection::ApplyLocalDescription(
return error;
}
}
// Remove unused channels if MediaContentDescription is rejected.
RemoveUnusedChannels(local_description()->description());
}
RTCError error = UpdateSessionState(type, cricket::CS_LOCAL);
error = UpdateSessionState(type, cricket::CS_LOCAL,
local_description()->description());
if (!error.ok()) {
return error;
}
@ -2246,6 +2252,10 @@ RTCError PeerConnection::ApplyRemoteDescription(
// |remote_description()|.
RTC_DCHECK(remote_description());
RTCError error = PushdownTransportDescription(cricket::CS_REMOTE, type);
if (!error.ok()) {
return error;
}
// Transport and Media channels will be created only when offer is set.
if (IsUnifiedPlan()) {
RTCError error = UpdateTransceiversAndDataChannels(
@ -2255,22 +2265,24 @@ RTCError PeerConnection::ApplyRemoteDescription(
return error;
}
} else {
// Media channels will be created only when offer is set. These may use new
// transports just created by PushdownTransportDescription.
if (type == SdpType::kOffer) {
// TODO(bugs.webrtc.org/4676) - Handle CreateChannel failure, as new local
// TODO(mallinath) - Handle CreateChannel failure, as new local
// description is applied. Restore back to old description.
RTCError error = CreateChannels(*remote_description()->description());
if (!error.ok()) {
return error;
}
}
// Remove unused channels if MediaContentDescription is rejected.
RemoveUnusedChannels(remote_description()->description());
}
// NOTE: Candidates allocation will be initiated only when SetLocalDescription
// is called.
RTCError error = UpdateSessionState(type, cricket::CS_REMOTE);
// NOTE: Candidates allocation will be initiated only when
// SetLocalDescription is called.
error = UpdateSessionState(type, cricket::CS_REMOTE,
remote_description()->description());
if (!error.ok()) {
return error;
}
@ -2574,14 +2586,10 @@ RTCError PeerConnection::UpdateTransceiverChannel(
} else {
if (!channel) {
if (transceiver->media_type() == cricket::MEDIA_TYPE_AUDIO) {
channel = CreateVoiceChannel(
content.name,
GetTransportNameForMediaSection(content.name, bundle_group));
channel = CreateVoiceChannel(content.name);
} else {
RTC_DCHECK_EQ(cricket::MEDIA_TYPE_VIDEO, transceiver->media_type());
channel = CreateVideoChannel(
content.name,
GetTransportNameForMediaSection(content.name, bundle_group));
channel = CreateVideoChannel(content.name);
}
if (!channel) {
LOG_AND_RETURN_ERROR(
@ -2607,8 +2615,7 @@ RTCError PeerConnection::UpdateDataChannel(
DestroyDataChannel();
} else {
if (!rtp_data_channel_ && !sctp_transport_) {
if (!CreateDataChannel(content.name, GetTransportNameForMediaSection(
content.name, bundle_group))) {
if (!CreateDataChannel(content.name)) {
LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
"Failed to create data channel.");
}
@ -2919,10 +2926,10 @@ bool PeerConnection::RemoveIceCandidates(
}
// Remove the candidates from the transport controller.
std::string error;
bool res = transport_controller_->RemoveRemoteCandidates(candidates, &error);
if (!res && !error.empty()) {
RTC_LOG(LS_ERROR) << "Error when removing remote candidates: " << error;
RTCError error = transport_controller_->RemoveRemoteCandidates(candidates);
if (!error.ok()) {
RTC_LOG(LS_ERROR) << "Error when removing remote candidates: "
<< error.message();
}
return true;
}
@ -3965,7 +3972,7 @@ rtc::Optional<std::string> PeerConnection::GetDataMid() const {
}
return rtp_data_channel_->content_name();
case cricket::DCT_SCTP:
return sctp_content_name_;
return sctp_mid_;
default:
return rtc::nullopt;
}
@ -4657,7 +4664,12 @@ bool PeerConnection::GetSctpSslRole(rtc::SSLRole* role) {
return false;
}
return transport_controller_->GetSslRole(*sctp_transport_name_, role);
auto dtls_role = transport_controller_->GetDtlsRole(*sctp_mid_);
if (dtls_role) {
*role = *dtls_role;
return true;
}
return false;
}
bool PeerConnection::GetSslRole(const std::string& content_name,
@ -4669,8 +4681,12 @@ bool PeerConnection::GetSslRole(const std::string& content_name,
return false;
}
return transport_controller_->GetSslRole(GetTransportName(content_name),
role);
auto dtls_role = transport_controller_->GetDtlsRole(content_name);
if (dtls_role) {
*role = *dtls_role;
return true;
}
return false;
}
void PeerConnection::SetSessionError(SessionError error,
@ -4682,42 +4698,16 @@ void PeerConnection::SetSessionError(SessionError error,
}
}
RTCError PeerConnection::UpdateSessionState(SdpType type,
cricket::ContentSource source) {
RTCError PeerConnection::UpdateSessionState(
SdpType type,
cricket::ContentSource source,
const cricket::SessionDescription* description) {
RTC_DCHECK_RUN_ON(signaling_thread());
// If there's already a pending error then no state transition should happen.
// But all call-sites should be verifying this before calling us!
RTC_DCHECK(session_error() == SessionError::kNone);
// If this is an answer then we know whether to BUNDLE or not. If both the
// local and remote side have agreed to BUNDLE, go ahead and enable it.
if (type == SdpType::kAnswer) {
const cricket::ContentGroup* local_bundle =
local_description()->description()->GetGroupByName(
cricket::GROUP_TYPE_BUNDLE);
const cricket::ContentGroup* remote_bundle =
remote_description()->description()->GetGroupByName(
cricket::GROUP_TYPE_BUNDLE);
if (local_bundle && remote_bundle) {
// The answerer decides the transport to bundle on.
const cricket::ContentGroup* answer_bundle =
(source == cricket::CS_LOCAL ? local_bundle : remote_bundle);
if (!EnableBundle(*answer_bundle)) {
LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
kEnableBundleFailed);
}
}
}
// Only push down the transport description after potentially enabling BUNDLE;
// we don't want to push down a description on a transport about to be
// destroyed.
RTCError error = PushdownTransportDescription(source, type);
if (!error.ok()) {
return error;
}
// If this is answer-ish we're ready to let media flow.
if (type == SdpType::kPrAnswer || type == SdpType::kAnswer) {
EnableSending();
@ -4740,7 +4730,7 @@ RTCError PeerConnection::UpdateSessionState(SdpType type,
// Update internal objects according to the session description's media
// descriptions.
error = PushdownMediaDescription(type, source);
RTCError error = PushdownMediaDescription(type, source);
if (!error.ok()) {
return error;
}
@ -4834,29 +4824,17 @@ RTCError PeerConnection::PushdownTransportDescription(
SdpType type) {
RTC_DCHECK_RUN_ON(signaling_thread());
const SessionDescriptionInterface* sdesc =
(source == cricket::CS_LOCAL ? local_description()
: remote_description());
RTC_DCHECK(sdesc);
for (const cricket::TransportInfo& tinfo :
sdesc->description()->transport_infos()) {
std::string error;
bool success;
if (source == cricket::CS_LOCAL) {
success = transport_controller_->SetLocalTransportDescription(
tinfo.content_name, tinfo.description, type, &error);
} else {
success = transport_controller_->SetRemoteTransportDescription(
tinfo.content_name, tinfo.description, type, &error);
}
if (!success) {
LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
"Failed to push down transport description for " +
tinfo.content_name + ": " + error);
}
if (source == cricket::CS_LOCAL) {
const SessionDescriptionInterface* sdesc = local_description();
RTC_DCHECK(sdesc);
return transport_controller_->SetLocalDescription(type,
sdesc->description());
} else {
const SessionDescriptionInterface* sdesc = remote_description();
RTC_DCHECK(sdesc);
return transport_controller_->SetRemoteDescription(type,
sdesc->description());
}
return RTCError::OK();
}
bool PeerConnection::GetTransportDescription(
@ -4875,71 +4853,6 @@ bool PeerConnection::GetTransportDescription(
return true;
}
bool PeerConnection::EnableBundle(const cricket::ContentGroup& bundle) {
const std::string* first_content_name = bundle.FirstContentName();
if (!first_content_name) {
RTC_LOG(LS_WARNING) << "Tried to BUNDLE with no contents.";
return false;
}
const std::string& transport_name = *first_content_name;
auto maybe_set_transport = [this, bundle,
transport_name](cricket::BaseChannel* ch) {
if (!ch || !bundle.HasContentName(ch->content_name())) {
return;
}
std::string old_transport_name = ch->transport_name();
if (old_transport_name == transport_name) {
RTC_LOG(LS_INFO) << "BUNDLE already enabled for " << ch->content_name()
<< " on " << transport_name << ".";
return;
}
cricket::DtlsTransportInternal* rtp_dtls_transport =
transport_controller_->CreateDtlsTransport(
transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP);
bool need_rtcp = (ch->rtcp_dtls_transport() != nullptr);
cricket::DtlsTransportInternal* rtcp_dtls_transport = nullptr;
if (need_rtcp) {
rtcp_dtls_transport = transport_controller_->CreateDtlsTransport(
transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTCP);
}
ch->SetTransports(rtp_dtls_transport, rtcp_dtls_transport);
RTC_LOG(LS_INFO) << "Enabled BUNDLE for " << ch->content_name() << " on "
<< transport_name << ".";
transport_controller_->DestroyDtlsTransport(
old_transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP);
// If the channel needs rtcp, it means that the channel used to have a
// rtcp transport which needs to be deleted now.
if (need_rtcp) {
transport_controller_->DestroyDtlsTransport(
old_transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTCP);
}
};
for (auto transceiver : transceivers_) {
maybe_set_transport(transceiver->internal()->channel());
}
maybe_set_transport(rtp_data_channel_);
// For SCTP, transport creation/deletion happens here instead of in the
// object itself.
if (sctp_transport_) {
RTC_DCHECK(sctp_transport_name_);
RTC_DCHECK(sctp_content_name_);
if (transport_name != *sctp_transport_name_ &&
bundle.HasContentName(*sctp_content_name_)) {
network_thread()->Invoke<void>(
RTC_FROM_HERE, rtc::Bind(&PeerConnection::ChangeSctpTransport_n, this,
transport_name));
}
}
return true;
}
cricket::IceConfig PeerConnection::ParseIceConfig(
const PeerConnectionInterface::RTCConfiguration& config) const {
cricket::ContinualGatheringPolicy gathering_policy;
@ -5081,6 +4994,17 @@ bool PeerConnection::ReadyToSendData() const {
sctp_ready_to_send_data_;
}
rtc::Optional<std::string> PeerConnection::sctp_transport_name() const {
if (sctp_mid_ && transport_controller_) {
auto dtls_transport = transport_controller_->GetDtlsTransport(*sctp_mid_);
if (dtls_transport) {
return dtls_transport->transport_name();
}
return rtc::Optional<std::string>();
}
return rtc::Optional<std::string>();
}
cricket::CandidateStatsList PeerConnection::GetPooledCandidateStats() const {
cricket::CandidateStatsList candidate_states_list;
port_allocator_->GetCandidateStatsFromPooledSessions(&candidate_states_list);
@ -5102,7 +5026,9 @@ std::map<std::string, std::string> PeerConnection::GetTransportNamesByMid()
rtp_data_channel_->transport_name();
}
if (sctp_transport_) {
transport_names_by_mid[*sctp_content_name_] = *sctp_transport_name_;
rtc::Optional<std::string> transport_name = sctp_transport_name();
RTC_DCHECK(transport_name);
transport_names_by_mid[*sctp_mid_] = *transport_name;
}
return transport_names_by_mid;
}
@ -5134,8 +5060,11 @@ PeerConnection::GetTransportStatsByNames(
bool PeerConnection::GetLocalCertificate(
const std::string& transport_name,
rtc::scoped_refptr<rtc::RTCCertificate>* certificate) {
return transport_controller_->GetLocalCertificate(transport_name,
certificate);
if (!certificate) {
return false;
}
*certificate = transport_controller_->GetLocalCertificate(transport_name);
return *certificate != nullptr;
}
std::unique_ptr<rtc::SSLCertChain> PeerConnection::GetRemoteSSLCertChain(
@ -5339,9 +5268,9 @@ bool PeerConnection::UseCandidate(const IceCandidateInterface* candidate) {
std::vector<cricket::Candidate> candidates;
candidates.push_back(candidate->candidate());
// Invoking BaseSession method to handle remote candidates.
std::string error;
if (transport_controller_->AddRemoteCandidates(content.name, candidates,
&error)) {
RTCError error =
transport_controller_->AddRemoteCandidates(content.name, candidates);
if (error.ok()) {
// Candidates successfully submitted for checking.
if (ice_connection_state_ == PeerConnectionInterface::kIceConnectionNew ||
ice_connection_state_ ==
@ -5357,10 +5286,8 @@ bool PeerConnection::UseCandidate(const IceCandidateInterface* candidate) {
SetIceConnectionState(PeerConnectionInterface::kIceConnectionChecking);
}
// TODO(bemasc): If state is Completed, go back to Connected.
} else {
if (!error.empty()) {
RTC_LOG(LS_WARNING) << error;
}
} else if (error.message()) {
RTC_LOG(LS_WARNING) << error.message();
}
return true;
}
@ -5384,25 +5311,6 @@ void PeerConnection::RemoveUnusedChannels(const SessionDescription* desc) {
}
}
std::string PeerConnection::GetTransportNameForMediaSection(
const std::string& mid,
const cricket::ContentGroup* bundle_group) const {
if (!bundle_group) {
return mid;
}
const std::string* first_content_name = bundle_group->FirstContentName();
if (!first_content_name) {
RTC_LOG(LS_WARNING) << "Tried to BUNDLE with no contents.";
return mid;
}
if (!bundle_group->HasContentName(mid)) {
RTC_LOG(LS_WARNING) << mid << " is not part of any bundle group";
return mid;
}
RTC_LOG(LS_INFO) << "Bundling " << mid << " on " << *first_content_name;
return *first_content_name;
}
RTCErrorOr<const cricket::ContentGroup*> PeerConnection::GetEarlyBundleGroup(
const SessionDescription& desc) const {
const cricket::ContentGroup* bundle_group = nullptr;
@ -5419,19 +5327,12 @@ RTCErrorOr<const cricket::ContentGroup*> PeerConnection::GetEarlyBundleGroup(
}
RTCError PeerConnection::CreateChannels(const SessionDescription& desc) {
auto bundle_group_or_error = GetEarlyBundleGroup(desc);
if (!bundle_group_or_error.ok()) {
return bundle_group_or_error.MoveError();
}
const cricket::ContentGroup* bundle_group = bundle_group_or_error.MoveValue();
// Creating the media channels and transport proxies.
// Creating the media channels. Transports should already have been created
// at this point.
const cricket::ContentInfo* voice = cricket::GetFirstAudioContent(&desc);
if (voice && !voice->rejected &&
!GetAudioTransceiver()->internal()->channel()) {
cricket::VoiceChannel* voice_channel = CreateVoiceChannel(
voice->name,
GetTransportNameForMediaSection(voice->name, bundle_group));
cricket::VoiceChannel* voice_channel = CreateVoiceChannel(voice->name);
if (!voice_channel) {
LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
"Failed to create voice channel.");
@ -5442,9 +5343,7 @@ RTCError PeerConnection::CreateChannels(const SessionDescription& desc) {
const cricket::ContentInfo* video = cricket::GetFirstVideoContent(&desc);
if (video && !video->rejected &&
!GetVideoTransceiver()->internal()->channel()) {
cricket::VideoChannel* video_channel = CreateVideoChannel(
video->name,
GetTransportNameForMediaSection(video->name, bundle_group));
cricket::VideoChannel* video_channel = CreateVideoChannel(video->name);
if (!video_channel) {
LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
"Failed to create video channel.");
@ -5455,8 +5354,7 @@ RTCError PeerConnection::CreateChannels(const SessionDescription& desc) {
const cricket::ContentInfo* data = cricket::GetFirstDataContent(&desc);
if (data_channel_type_ != cricket::DCT_NONE && data && !data->rejected &&
!rtp_data_channel_ && !sctp_transport_) {
if (!CreateDataChannel(data->name, GetTransportNameForMediaSection(
data->name, bundle_group))) {
if (!CreateDataChannel(data->name)) {
LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
"Failed to create data channel.");
}
@ -5467,37 +5365,25 @@ RTCError PeerConnection::CreateChannels(const SessionDescription& desc) {
// TODO(steveanton): Perhaps this should be managed by the RtpTransceiver.
cricket::VoiceChannel* PeerConnection::CreateVoiceChannel(
const std::string& mid,
const std::string& transport_name) {
cricket::DtlsTransportInternal* rtp_dtls_transport =
transport_controller_->CreateDtlsTransport(
transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP);
cricket::DtlsTransportInternal* rtcp_dtls_transport = nullptr;
if (configuration_.rtcp_mux_policy !=
PeerConnectionInterface::kRtcpMuxPolicyRequire) {
rtcp_dtls_transport = transport_controller_->CreateDtlsTransport(
transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTCP);
}
const std::string& mid) {
RtpTransportInternal* rtp_transport =
transport_controller_->GetRtpTransport(mid);
RTC_DCHECK(rtp_transport);
cricket::VoiceChannel* voice_channel = channel_manager()->CreateVoiceChannel(
call_.get(), configuration_.media_config, rtp_dtls_transport,
rtcp_dtls_transport, signaling_thread(), mid, SrtpRequired(),
audio_options_);
call_.get(), configuration_.media_config, rtp_transport,
signaling_thread(), mid, SrtpRequired(),
factory_->options().crypto_options, audio_options_);
if (!voice_channel) {
transport_controller_->DestroyDtlsTransport(
transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP);
if (rtcp_dtls_transport) {
transport_controller_->DestroyDtlsTransport(
transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTCP);
}
return nullptr;
}
voice_channel->SignalRtcpMuxFullyActive.connect(
this, &PeerConnection::DestroyRtcpTransport_n);
voice_channel->SignalDtlsSrtpSetupFailure.connect(
this, &PeerConnection::OnDtlsSrtpSetupFailure);
voice_channel->SignalSentPacket.connect(this,
&PeerConnection::OnSentPacket_w);
voice_channel->SetRtpTransport(rtp_transport);
if (factory_->options().disable_encryption) {
voice_channel->DisableEncryption(true);
}
if (uma_observer_) {
voice_channel->SetMetricsObserver(uma_observer_);
}
@ -5507,38 +5393,25 @@ cricket::VoiceChannel* PeerConnection::CreateVoiceChannel(
// TODO(steveanton): Perhaps this should be managed by the RtpTransceiver.
cricket::VideoChannel* PeerConnection::CreateVideoChannel(
const std::string& mid,
const std::string& transport_name) {
cricket::DtlsTransportInternal* rtp_dtls_transport =
transport_controller_->CreateDtlsTransport(
transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP);
cricket::DtlsTransportInternal* rtcp_dtls_transport = nullptr;
if (configuration_.rtcp_mux_policy !=
PeerConnectionInterface::kRtcpMuxPolicyRequire) {
rtcp_dtls_transport = transport_controller_->CreateDtlsTransport(
transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTCP);
}
const std::string& mid) {
RtpTransportInternal* rtp_transport =
transport_controller_->GetRtpTransport(mid);
RTC_DCHECK(rtp_transport);
cricket::VideoChannel* video_channel = channel_manager()->CreateVideoChannel(
call_.get(), configuration_.media_config, rtp_dtls_transport,
rtcp_dtls_transport, signaling_thread(), mid, SrtpRequired(),
video_options_);
call_.get(), configuration_.media_config, rtp_transport,
signaling_thread(), mid, SrtpRequired(),
factory_->options().crypto_options, video_options_);
if (!video_channel) {
transport_controller_->DestroyDtlsTransport(
transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP);
if (rtcp_dtls_transport) {
transport_controller_->DestroyDtlsTransport(
transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTCP);
}
return nullptr;
}
video_channel->SignalRtcpMuxFullyActive.connect(
this, &PeerConnection::DestroyRtcpTransport_n);
video_channel->SignalDtlsSrtpSetupFailure.connect(
this, &PeerConnection::OnDtlsSrtpSetupFailure);
video_channel->SignalSentPacket.connect(this,
&PeerConnection::OnSentPacket_w);
video_channel->SetRtpTransport(rtp_transport);
if (factory_->options().disable_encryption) {
video_channel->DisableEncryption(true);
}
if (uma_observer_) {
video_channel->SetMetricsObserver(uma_observer_);
}
@ -5546,8 +5419,7 @@ cricket::VideoChannel* PeerConnection::CreateVideoChannel(
return video_channel;
}
bool PeerConnection::CreateDataChannel(const std::string& mid,
const std::string& transport_name) {
bool PeerConnection::CreateDataChannel(const std::string& mid) {
bool sctp = (data_channel_type_ == cricket::DCT_SCTP);
if (sctp) {
if (!sctp_factory_) {
@ -5557,44 +5429,31 @@ bool PeerConnection::CreateDataChannel(const std::string& mid,
return false;
}
if (!network_thread()->Invoke<bool>(
RTC_FROM_HERE, rtc::Bind(&PeerConnection::CreateSctpTransport_n,
this, mid, transport_name))) {
RTC_FROM_HERE,
rtc::Bind(&PeerConnection::CreateSctpTransport_n, this, mid))) {
return false;
}
for (const auto& channel : sctp_data_channels_) {
channel->OnTransportChannelCreated();
}
} else {
cricket::DtlsTransportInternal* rtp_dtls_transport =
transport_controller_->CreateDtlsTransport(
transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP);
cricket::DtlsTransportInternal* rtcp_dtls_transport = nullptr;
if (configuration_.rtcp_mux_policy !=
PeerConnectionInterface::kRtcpMuxPolicyRequire) {
rtcp_dtls_transport = transport_controller_->CreateDtlsTransport(
transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTCP);
}
RtpTransportInternal* rtp_transport =
transport_controller_->GetRtpTransport(mid);
RTC_DCHECK(rtp_transport);
rtp_data_channel_ = channel_manager()->CreateRtpDataChannel(
configuration_.media_config, rtp_dtls_transport, rtcp_dtls_transport,
signaling_thread(), mid, SrtpRequired());
configuration_.media_config, rtp_transport, signaling_thread(), mid,
SrtpRequired(), factory_->options().crypto_options);
if (!rtp_data_channel_) {
transport_controller_->DestroyDtlsTransport(
transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP);
if (rtcp_dtls_transport) {
transport_controller_->DestroyDtlsTransport(
transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTCP);
}
return false;
}
rtp_data_channel_->SignalRtcpMuxFullyActive.connect(
this, &PeerConnection::DestroyRtcpTransport_n);
rtp_data_channel_->SignalDtlsSrtpSetupFailure.connect(
this, &PeerConnection::OnDtlsSrtpSetupFailure);
rtp_data_channel_->SignalSentPacket.connect(
this, &PeerConnection::OnSentPacket_w);
rtp_data_channel_->SetRtpTransport(rtp_transport);
if (factory_->options().disable_encryption) {
rtp_data_channel_->DisableEncryption(true);
}
if (uma_observer_) {
rtp_data_channel_->SetMetricsObserver(uma_observer_);
}
@ -5615,13 +5474,12 @@ Call::Stats PeerConnection::GetCallStats() {
}
}
bool PeerConnection::CreateSctpTransport_n(const std::string& content_name,
const std::string& transport_name) {
bool PeerConnection::CreateSctpTransport_n(const std::string& mid) {
RTC_DCHECK(network_thread()->IsCurrent());
RTC_DCHECK(sctp_factory_);
cricket::DtlsTransportInternal* tc =
transport_controller_->CreateDtlsTransport_n(
transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP);
transport_controller_->GetDtlsTransport(mid);
RTC_DCHECK(tc);
sctp_transport_ = sctp_factory_->CreateSctpTransport(tc);
RTC_DCHECK(sctp_transport_);
sctp_invoker_.reset(new rtc::AsyncInvoker());
@ -5631,32 +5489,15 @@ bool PeerConnection::CreateSctpTransport_n(const std::string& content_name,
this, &PeerConnection::OnSctpTransportDataReceived_n);
sctp_transport_->SignalStreamClosedRemotely.connect(
this, &PeerConnection::OnSctpStreamClosedRemotely_n);
sctp_transport_name_ = transport_name;
sctp_content_name_ = content_name;
return true;
}
void PeerConnection::ChangeSctpTransport_n(const std::string& transport_name) {
RTC_DCHECK(network_thread()->IsCurrent());
RTC_DCHECK(sctp_transport_);
RTC_DCHECK(sctp_transport_name_);
std::string old_sctp_transport_name = *sctp_transport_name_;
sctp_transport_name_ = transport_name;
cricket::DtlsTransportInternal* tc =
transport_controller_->CreateDtlsTransport_n(
transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP);
sctp_mid_ = mid;
sctp_transport_->SetTransportChannel(tc);
transport_controller_->DestroyDtlsTransport_n(
old_sctp_transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP);
return true;
}
void PeerConnection::DestroySctpTransport_n() {
RTC_DCHECK(network_thread()->IsCurrent());
sctp_transport_.reset(nullptr);
transport_controller_->DestroyDtlsTransport_n(
*sctp_transport_name_, cricket::ICE_CANDIDATE_COMPONENT_RTP);
sctp_content_name_.reset();
sctp_transport_name_.reset();
sctp_mid_.reset();
sctp_invoker_.reset(nullptr);
sctp_ready_to_send_data_ = false;
}
@ -5989,7 +5830,7 @@ bool PeerConnection::ReadyToUseRemoteCandidate(
if (transport_name.empty()) {
return false;
}
return transport_controller_->ReadyForRemoteCandidates(transport_name);
return true;
}
bool PeerConnection::SrtpRequired() const {
@ -6022,10 +5863,13 @@ void PeerConnection::ReportTransportStats() {
media_types_by_transport_name[rtp_data_channel()->transport_name()].insert(
cricket::MEDIA_TYPE_DATA);
}
if (sctp_transport_name_) {
media_types_by_transport_name[*sctp_transport_name_].insert(
rtc::Optional<std::string> transport_name = sctp_transport_name();
if (transport_name) {
media_types_by_transport_name[*transport_name].insert(
cricket::MEDIA_TYPE_DATA);
}
for (const auto& entry : media_types_by_transport_name) {
const std::string& transport_name = entry.first;
const std::set<cricket::MediaType> media_types = entry.second;
@ -6145,22 +5989,15 @@ const std::string PeerConnection::GetTransportName(
return channel->transport_name();
}
if (sctp_transport_) {
RTC_DCHECK(sctp_content_name_);
RTC_DCHECK(sctp_transport_name_);
if (content_name == *sctp_content_name_) {
return *sctp_transport_name_;
RTC_DCHECK(sctp_mid_);
if (content_name == *sctp_mid_) {
return *sctp_transport_name();
}
}
// Return an empty string if failed to retrieve the transport name.
return "";
}
void PeerConnection::DestroyRtcpTransport_n(const std::string& transport_name) {
RTC_DCHECK(network_thread()->IsCurrent());
transport_controller_->DestroyDtlsTransport_n(
transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTCP);
}
void PeerConnection::DestroyTransceiverChannel(
rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>
transceiver) {
@ -6195,13 +6032,6 @@ void PeerConnection::DestroyDataChannel() {
void PeerConnection::DestroyBaseChannel(cricket::BaseChannel* channel) {
RTC_DCHECK(channel);
RTC_DCHECK(channel->rtp_dtls_transport());
// Need to cache these before destroying the base channel so that we do not
// access uninitialized memory.
const std::string transport_name =
channel->rtp_dtls_transport()->transport_name();
const bool need_to_delete_rtcp = (channel->rtcp_dtls_transport() != nullptr);
switch (channel->media_type()) {
case cricket::MEDIA_TYPE_AUDIO:
@ -6220,14 +6050,23 @@ void PeerConnection::DestroyBaseChannel(cricket::BaseChannel* channel) {
RTC_NOTREACHED() << "Unknown media type: " << channel->media_type();
break;
}
}
// |channel| can no longer be used.
void PeerConnection::OnRtpTransportChanged(
const std::string& mid,
RtpTransportInternal* rtp_transport) {
auto base_channel = GetChannel(mid);
if (base_channel) {
base_channel->SetRtpTransport(rtp_transport);
}
}
transport_controller_->DestroyDtlsTransport(
transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP);
if (need_to_delete_rtcp) {
transport_controller_->DestroyDtlsTransport(
transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTCP);
void PeerConnection::OnDtlsTransportChanged(
const std::string& mid,
cricket::DtlsTransportInternal* dtls_transport) {
if (sctp_transport_) {
RTC_DCHECK(mid == sctp_mid_);
sctp_transport_->SetTransportChannel(dtls_transport);
}
}

View File

@ -20,6 +20,7 @@
#include "api/peerconnectioninterface.h"
#include "api/turncustomizer.h"
#include "pc/iceserverparsing.h"
#include "pc/jseptransportcontroller.h"
#include "pc/peerconnectionfactory.h"
#include "pc/peerconnectioninternal.h"
#include "pc/rtcstatscollector.h"
@ -208,7 +209,7 @@ class PeerConnection : public PeerConnectionInternal,
std::string session_id() const override { return session_id_; }
bool initial_offerer() const override {
return initial_offerer_ && *initial_offerer_;
return transport_controller_ && transport_controller_->initial_offerer();
}
std::vector<
@ -234,12 +235,10 @@ class PeerConnection : public PeerConnectionInternal,
}
rtc::Optional<std::string> sctp_content_name() const override {
return sctp_content_name_;
return sctp_mid_;
}
rtc::Optional<std::string> sctp_transport_name() const override {
return sctp_transport_name_;
}
rtc::Optional<std::string> sctp_transport_name() const override;
cricket::CandidateStatsList GetPooledCandidateStats() const override;
std::map<std::string, std::string> GetTransportNamesByMid() const override;
@ -709,7 +708,7 @@ class PeerConnection : public PeerConnectionInternal,
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate);
void OnDtlsSrtpSetupFailure(cricket::BaseChannel*, bool rtcp);
cricket::TransportController* transport_controller() const {
JsepTransportController* transport_controller() const {
return transport_controller_.get();
}
@ -727,7 +726,9 @@ class PeerConnection : public PeerConnectionInternal,
// Updates the error state, signaling if necessary.
void SetSessionError(SessionError error, const std::string& error_desc);
RTCError UpdateSessionState(SdpType type, cricket::ContentSource source);
RTCError UpdateSessionState(SdpType type,
cricket::ContentSource source,
const cricket::SessionDescription* description);
// Push the media parts of the local or remote session description
// down to all of the channels.
RTCError PushdownMediaDescription(SdpType type,
@ -744,18 +745,6 @@ class PeerConnection : public PeerConnectionInternal,
const std::string& content_name,
cricket::TransportDescription* info);
// Returns the transport name for the given media section identified by |mid|.
// If BUNDLE is enabled and the media section is part of the bundle group,
// the transport name will be the first mid in the bundle group. Otherwise,
// the transport name will be the mid of the media section.
std::string GetTransportNameForMediaSection(
const std::string& mid,
const cricket::ContentGroup* bundle_group) const;
// Cause all the BaseChannels in the bundle group to have the same
// transport channel.
bool EnableBundle(const cricket::ContentGroup& bundle);
// Enables media channels to allow sending of media.
// This enables media to flow on all configured audio/video channels and the
// RtpDataChannel.
@ -792,17 +781,12 @@ class PeerConnection : public PeerConnectionInternal,
const cricket::SessionDescription& desc) const;
// Helper methods to create media channels.
cricket::VoiceChannel* CreateVoiceChannel(const std::string& mid,
const std::string& transport_name);
cricket::VideoChannel* CreateVideoChannel(const std::string& mid,
const std::string& transport_name);
bool CreateDataChannel(const std::string& mid,
const std::string& transport_name);
cricket::VoiceChannel* CreateVoiceChannel(const std::string& mid);
cricket::VideoChannel* CreateVideoChannel(const std::string& mid);
bool CreateDataChannel(const std::string& mid);
bool CreateSctpTransport_n(const std::string& content_name,
const std::string& transport_name);
bool CreateSctpTransport_n(const std::string& mid);
// For bundling.
void ChangeSctpTransport_n(const std::string& transport_name);
void DestroySctpTransport_n();
// SctpTransport signal handlers. Needed to marshal signals from the network
// to signaling thread.
@ -846,7 +830,7 @@ class PeerConnection : public PeerConnectionInternal,
// this session.
bool SrtpRequired() const;
// TransportController signal handlers.
// JsepTransportController signal handlers.
void OnTransportControllerConnectionState(cricket::IceConnectionState state);
void OnTransportControllerGatheringState(cricket::IceGatheringState state);
void OnTransportControllerCandidatesGathered(
@ -880,8 +864,6 @@ class PeerConnection : public PeerConnectionInternal,
const std::string GetTransportName(const std::string& content_name);
void DestroyRtcpTransport_n(const std::string& transport_name);
// Destroys and clears the BaseChannel associated with the given transceiver,
// if such channel is set.
void DestroyTransceiverChannel(
@ -895,6 +877,12 @@ class PeerConnection : public PeerConnectionInternal,
// method is called.
void DestroyBaseChannel(cricket::BaseChannel* channel);
void OnRtpTransportChanged(const std::string& mid,
RtpTransportInternal* rtp_transport);
void OnDtlsTransportChanged(const std::string& mid,
cricket::DtlsTransportInternal* dtls_transport);
sigslot::signal1<DataChannel*> SignalDataChannelCreated_;
// Storing the factory as a scoped reference pointer ensures that the memory
@ -957,20 +945,16 @@ class PeerConnection : public PeerConnectionInternal,
std::string session_error_desc_;
std::string session_id_;
rtc::Optional<bool> initial_offerer_;
std::unique_ptr<cricket::TransportController> transport_controller_;
std::unique_ptr<JsepTransportController> transport_controller_;
std::unique_ptr<cricket::SctpTransportInternalFactory> sctp_factory_;
// |rtp_data_channel_| is used if in RTP data channel mode, |sctp_transport_|
// when using SCTP.
cricket::RtpDataChannel* rtp_data_channel_ = nullptr;
std::unique_ptr<cricket::SctpTransportInternal> sctp_transport_;
// |sctp_transport_name_| keeps track of what DTLS transport the SCTP
// transport is using (which can change due to bundling).
rtc::Optional<std::string> sctp_transport_name_;
// |sctp_content_name_| is the content name (MID) in SDP.
rtc::Optional<std::string> sctp_content_name_;
// |sctp_mid_| is the content name (MID) in SDP.
rtc::Optional<std::string> sctp_mid_;
// Value cached on signaling thread. Only updated when SctpReadyToSendData
// fires on the signaling thread.
bool sctp_ready_to_send_data_ = false;

View File

@ -67,12 +67,14 @@ class PeerConnectionWrapperForBundleTest : public PeerConnectionWrapper {
return false;
}
rtc::PacketTransportInternal* voice_rtp_transport_channel() {
return (voice_channel() ? voice_channel()->rtp_dtls_transport() : nullptr);
rtc::PacketTransportInternal* voice_rtp_transport() {
return (voice_channel() ? voice_channel()->rtp_packet_transport()
: nullptr);
}
rtc::PacketTransportInternal* voice_rtcp_transport_channel() {
return (voice_channel() ? voice_channel()->rtcp_dtls_transport() : nullptr);
rtc::PacketTransportInternal* voice_rtcp_transport() {
return (voice_channel() ? voice_channel()->rtcp_packet_transport()
: nullptr);
}
cricket::VoiceChannel* voice_channel() {
@ -86,12 +88,14 @@ class PeerConnectionWrapperForBundleTest : public PeerConnectionWrapper {
return nullptr;
}
rtc::PacketTransportInternal* video_rtp_transport_channel() {
return (video_channel() ? video_channel()->rtp_dtls_transport() : nullptr);
rtc::PacketTransportInternal* video_rtp_transport() {
return (video_channel() ? video_channel()->rtp_packet_transport()
: nullptr);
}
rtc::PacketTransportInternal* video_rtcp_transport_channel() {
return (video_channel() ? video_channel()->rtcp_dtls_transport() : nullptr);
rtc::PacketTransportInternal* video_rtcp_transport() {
return (video_channel() ? video_channel()->rtcp_packet_transport()
: nullptr);
}
cricket::VideoChannel* video_channel() {
@ -348,6 +352,24 @@ TEST_P(PeerConnectionBundleTest,
EXPECT_EQ(0u, caller->observer()->GetCandidatesByMline(1).size());
}
// It will fail if the offerer uses the mux-BUNDLE policy but the answerer
// doesn't support BUNDLE.
TEST_P(PeerConnectionBundleTest, MaxBundleNotSupportedInAnswer) {
RTCConfiguration config;
config.bundle_policy = BundlePolicy::kBundlePolicyMaxBundle;
auto caller = CreatePeerConnectionWithAudioVideo(config);
auto callee = CreatePeerConnectionWithAudioVideo();
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
bool equal_before =
(caller->voice_rtp_transport() == caller->video_rtp_transport());
EXPECT_EQ(true, equal_before);
RTCOfferAnswerOptions options;
options.use_rtp_mux = false;
EXPECT_FALSE(
caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal(options)));
}
// The following parameterized test verifies that an offer/answer with varying
// bundle policies and either bundle in the answer or not will produce the
// expected RTP transports for audio and video. In particular, for bundling we
@ -393,16 +415,16 @@ TEST_P(PeerConnectionBundleMatrixTest,
auto callee = CreatePeerConnectionWithAudioVideo();
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
bool equal_before = (caller->voice_rtp_transport_channel() ==
caller->video_rtp_transport_channel());
bool equal_before =
(caller->voice_rtp_transport() == caller->video_rtp_transport());
EXPECT_EQ(expected_same_before_, equal_before);
RTCOfferAnswerOptions options;
options.use_rtp_mux = (bundle_included_ == BundleIncluded::kBundleInAnswer);
ASSERT_TRUE(
caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal(options)));
bool equal_after = (caller->voice_rtp_transport_channel() ==
caller->video_rtp_transport_channel());
bool equal_after =
(caller->voice_rtp_transport() == caller->video_rtp_transport());
EXPECT_EQ(expected_same_after_, equal_after);
}
@ -426,10 +448,6 @@ INSTANTIATE_TEST_CASE_P(
BundleIncluded::kBundleInAnswer,
true,
true),
std::make_tuple(BundlePolicy::kBundlePolicyMaxBundle,
BundleIncluded::kBundleNotInAnswer,
true,
true),
std::make_tuple(BundlePolicy::kBundlePolicyMaxCompat,
BundleIncluded::kBundleInAnswer,
false,
@ -454,13 +472,11 @@ TEST_P(PeerConnectionBundleTest,
ASSERT_TRUE(callee->SetRemoteDescription(
caller->CreateOfferAndSetAsLocal(options_with_bundle)));
EXPECT_EQ(callee->voice_rtp_transport_channel(),
callee->video_rtp_transport_channel());
EXPECT_EQ(callee->voice_rtp_transport(), callee->video_rtp_transport());
ASSERT_TRUE(callee->SetLocalDescription(callee->CreateAnswer()));
EXPECT_EQ(callee->voice_rtp_transport_channel(),
callee->video_rtp_transport_channel());
EXPECT_EQ(callee->voice_rtp_transport(), callee->video_rtp_transport());
}
TEST_P(PeerConnectionBundleTest,
@ -496,8 +512,8 @@ TEST_P(PeerConnectionBundleTest,
ASSERT_TRUE(
caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal(options)));
EXPECT_FALSE(caller->voice_rtp_transport_channel());
EXPECT_TRUE(caller->video_rtp_transport_channel());
EXPECT_FALSE(caller->voice_rtp_transport());
EXPECT_TRUE(caller->video_rtp_transport());
}
// When requiring RTCP multiplexing, the PeerConnection never makes RTCP
@ -510,19 +526,18 @@ TEST_P(PeerConnectionBundleTest, NeverCreateRtcpTransportWithRtcpMuxRequired) {
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
EXPECT_FALSE(caller->voice_rtcp_transport_channel());
EXPECT_FALSE(caller->video_rtcp_transport_channel());
EXPECT_FALSE(caller->voice_rtcp_transport());
EXPECT_FALSE(caller->video_rtcp_transport());
ASSERT_TRUE(
caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
EXPECT_FALSE(caller->voice_rtcp_transport_channel());
EXPECT_FALSE(caller->video_rtcp_transport_channel());
EXPECT_FALSE(caller->voice_rtcp_transport());
EXPECT_FALSE(caller->video_rtcp_transport());
}
// When negotiating RTCP multiplexing, the PeerConnection makes RTCP transport
// channels when the offer is sent, but will destroy them once the remote answer
// is set.
// When negotiating RTCP multiplexing, the PeerConnection makes RTCP transports
// when the offer is sent, but will destroy them once the remote answer is set.
TEST_P(PeerConnectionBundleTest,
CreateRtcpTransportOnlyBeforeAnswerWithRtcpMuxNegotiate) {
RTCConfiguration config;
@ -532,14 +547,14 @@ TEST_P(PeerConnectionBundleTest,
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
EXPECT_TRUE(caller->voice_rtcp_transport_channel());
EXPECT_TRUE(caller->video_rtcp_transport_channel());
EXPECT_TRUE(caller->voice_rtcp_transport());
EXPECT_TRUE(caller->video_rtcp_transport());
ASSERT_TRUE(
caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
EXPECT_FALSE(caller->voice_rtcp_transport_channel());
EXPECT_FALSE(caller->video_rtcp_transport_channel());
EXPECT_FALSE(caller->voice_rtcp_transport());
EXPECT_FALSE(caller->video_rtcp_transport());
}
TEST_P(PeerConnectionBundleTest, FailToSetDescriptionWithBundleAndNoRtcpMux) {
@ -588,7 +603,7 @@ TEST_P(PeerConnectionBundleTest,
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
ASSERT_TRUE(
caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal(options)));
// The way the *_WAIT checks work is they only wait if the condition fails,
// which does not help in the case where state is not changing. This is
@ -624,7 +639,7 @@ TEST_P(PeerConnectionBundleTest, BundleOnFirstMidInAnswer) {
ASSERT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
auto* old_video_transport = caller->video_rtp_transport_channel();
auto* old_video_transport = caller->video_rtp_transport();
auto answer = callee->CreateAnswer();
auto* old_bundle_group =
@ -640,9 +655,8 @@ TEST_P(PeerConnectionBundleTest, BundleOnFirstMidInAnswer) {
ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
EXPECT_EQ(old_video_transport, caller->video_rtp_transport_channel());
EXPECT_EQ(caller->voice_rtp_transport_channel(),
caller->video_rtp_transport_channel());
EXPECT_EQ(old_video_transport, caller->video_rtp_transport());
EXPECT_EQ(caller->voice_rtp_transport(), caller->video_rtp_transport());
}
INSTANTIATE_TEST_CASE_P(PeerConnectionBundleTest,

View File

@ -200,7 +200,9 @@ class PeerConnectionIceBaseTest : public ::testing::Test {
if (transceiver->media_type() == cricket::MEDIA_TYPE_AUDIO) {
cricket::BaseChannel* channel = transceiver->internal()->channel();
if (channel) {
return channel->rtp_dtls_transport()->ice_transport()->GetIceRole();
auto dtls_transport = static_cast<cricket::DtlsTransportInternal*>(
channel->rtp_packet_transport());
return dtls_transport->ice_transport()->GetIceRole();
}
}
}

View File

@ -2342,8 +2342,10 @@ TEST_P(PeerConnectionIntegrationTest, GetCaptureStartNtpTimeWithOldStatsApi) {
// 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)->
CaptureStartNtpTime() > 0, 2 * kMaxWaitForFramesMs);
EXPECT_TRUE_WAIT(
callee()->OldGetStatsForTrack(remote_audio_track)->CaptureStartNtpTime() >
0,
2 * kMaxWaitForFramesMs);
}
// Test that we can get stats (using the new stats implemnetation) for

View File

@ -325,16 +325,6 @@ PeerConnectionFactory::CreateAudioTrack(const std::string& id,
return AudioTrackProxy::Create(signaling_thread_, track);
}
cricket::TransportController* PeerConnectionFactory::CreateTransportController(
cricket::PortAllocator* port_allocator,
bool redetermine_role_on_ice_restart,
RtcEventLog* event_log) {
RTC_DCHECK(signaling_thread_->IsCurrent());
return new cricket::TransportController(
signaling_thread_, network_thread_, port_allocator,
redetermine_role_on_ice_restart, options_.crypto_options, event_log);
}
std::unique_ptr<cricket::SctpTransportInternalFactory>
PeerConnectionFactory::CreateSctpTransportInternalFactory() {
#ifdef HAVE_SCTP

View File

@ -88,11 +88,6 @@ class PeerConnectionFactory : public PeerConnectionFactoryInterface {
bool StartAecDump(rtc::PlatformFile file, int64_t max_size_bytes) override;
void StopAecDump() override;
virtual cricket::TransportController* CreateTransportController(
cricket::PortAllocator* port_allocator,
bool redetermine_role_on_ice_restart,
RtcEventLog* event_log = nullptr);
virtual std::unique_ptr<cricket::SctpTransportInternalFactory>
CreateSctpTransportInternalFactory();

View File

@ -667,18 +667,7 @@ class PeerConnectionFactoryForTest : public webrtc::PeerConnectionFactory {
std::move(call_factory),
std::move(event_log_factory)) {}
cricket::TransportController* CreateTransportController(
cricket::PortAllocator* port_allocator,
bool redetermine_role_on_ice_restart,
webrtc::RtcEventLog* event_log = nullptr) override {
transport_controller = new cricket::TransportController(
rtc::Thread::Current(), rtc::Thread::Current(), port_allocator,
redetermine_role_on_ice_restart, rtc::CryptoOptions(), event_log);
return transport_controller;
}
rtc::scoped_refptr<FakeAudioCaptureModule> fake_audio_capture_module_;
cricket::TransportController* transport_controller;
};
// TODO(steveanton): Convert to use the new PeerConnectionWrapper.
@ -2318,8 +2307,7 @@ TEST_P(PeerConnectionInterfaceTest, ReceiveFireFoxOffer) {
content =
cricket::GetFirstDataContent(pc_->local_description()->description());
ASSERT_TRUE(content != NULL);
// Expected to fail since it's using an incompatible format.
EXPECT_TRUE(content->rejected);
EXPECT_FALSE(content->rejected);
#endif
}
@ -2341,7 +2329,7 @@ TEST_P(PeerConnectionInterfaceTest, DtlsSdesFallbackNotSupported) {
std::unique_ptr<SessionDescriptionInterface> desc(
webrtc::CreateSessionDescription(SdpType::kOffer, kDtlsSdesFallbackSdp,
nullptr));
EXPECT_FALSE(DoSetSessionDescription(std::move(desc), false));
EXPECT_FALSE(DoSetSessionDescription(std::move(desc), /*local=*/false));
}
// Test that we can create an audio only offer and receive an answer with a

View File

@ -16,6 +16,7 @@
#include "media/base/fakemediaengine.h"
#include "media/base/rtpdataengine.h"
#include "media/engine/fakewebrtccall.h"
#include "p2p/base/fakedtlstransport.h"
#include "pc/audiotrack.h"
#include "pc/channelmanager.h"
#include "pc/localaudiosource.h"
@ -24,7 +25,6 @@
#include "pc/rtpreceiver.h"
#include "pc/rtpsender.h"
#include "pc/streamcollection.h"
#include "pc/test/faketransportcontroller.h"
#include "pc/test/fakevideotracksource.h"
#include "pc/videotrack.h"
#include "pc/videotracksource.h"
@ -69,17 +69,18 @@ class RtpSenderReceiverTest : public testing::Test,
// Create channels to be used by the RtpSenders and RtpReceivers.
channel_manager_.Init();
bool srtp_required = true;
cricket::DtlsTransportInternal* rtp_transport =
fake_transport_controller_.CreateDtlsTransport(
cricket::CN_AUDIO, cricket::ICE_CANDIDATE_COMPONENT_RTP);
rtp_dtls_transport_ = rtc::MakeUnique<cricket::FakeDtlsTransport>(
"fake_dtls_transport", cricket::ICE_CANDIDATE_COMPONENT_RTP);
rtp_transport_ = CreateDtlsSrtpTransport();
voice_channel_ = channel_manager_.CreateVoiceChannel(
&fake_call_, cricket::MediaConfig(),
rtp_transport, nullptr, rtc::Thread::Current(),
cricket::CN_AUDIO, srtp_required, cricket::AudioOptions());
&fake_call_, cricket::MediaConfig(), rtp_transport_.get(),
rtc::Thread::Current(), cricket::CN_AUDIO, srtp_required,
rtc::CryptoOptions(), cricket::AudioOptions());
video_channel_ = channel_manager_.CreateVideoChannel(
&fake_call_, cricket::MediaConfig(),
rtp_transport, nullptr, rtc::Thread::Current(),
cricket::CN_VIDEO, srtp_required, cricket::VideoOptions());
&fake_call_, cricket::MediaConfig(), rtp_transport_.get(),
rtc::Thread::Current(), cricket::CN_VIDEO, srtp_required,
rtc::CryptoOptions(), cricket::VideoOptions());
voice_channel_->Enable(true);
video_channel_->Enable(true);
voice_media_channel_ = media_engine_->GetVoiceChannel(0);
@ -111,6 +112,18 @@ class RtpSenderReceiverTest : public testing::Test,
cricket::StreamParams::CreateLegacy(kVideoSsrc2));
}
std::unique_ptr<webrtc::RtpTransportInternal> CreateDtlsSrtpTransport() {
auto rtp_transport =
rtc::MakeUnique<webrtc::RtpTransport>(/*rtcp_mux_required=*/true);
auto srtp_transport =
rtc::MakeUnique<webrtc::SrtpTransport>(std::move(rtp_transport));
auto dtls_srtp_transport =
rtc::MakeUnique<webrtc::DtlsSrtpTransport>(std::move(srtp_transport));
dtls_srtp_transport->SetDtlsTransports(rtp_dtls_transport_.get(),
/*rtcp_dtls_transport=*/nullptr);
return dtls_srtp_transport;
}
// Needed to use DTMF sender.
void AddDtmfCodec() {
cricket::AudioSendParameters params;
@ -268,9 +281,12 @@ class RtpSenderReceiverTest : public testing::Test,
rtc::Thread* const network_thread_;
rtc::Thread* const worker_thread_;
webrtc::RtcEventLogNullImpl event_log_;
// The |rtp_dtls_transport_| and |rtp_transport_| should be destroyed after
// the |channel_manager|.
std::unique_ptr<cricket::DtlsTransportInternal> rtp_dtls_transport_;
std::unique_ptr<webrtc::RtpTransportInternal> rtp_transport_;
// |media_engine_| is actually owned by |channel_manager_|.
cricket::FakeMediaEngine* media_engine_;
cricket::FakeTransportController fake_transport_controller_;
cricket::ChannelManager channel_manager_;
cricket::FakeCall fake_call_;
cricket::VoiceChannel* voice_channel_;

View File

@ -134,26 +134,6 @@ bool RtpTransport::SendPacket(bool rtcp,
return true;
}
bool RtpTransport::HandlesPacket(const uint8_t* data, size_t len) {
return bundle_filter_.DemuxPacket(data, len);
}
bool RtpTransport::HandlesPayloadType(int payload_type) const {
return bundle_filter_.FindPayloadType(payload_type);
}
void RtpTransport::AddHandledPayloadType(int payload_type) {
bundle_filter_.AddPayloadType(payload_type);
}
PacketTransportInterface* RtpTransport::GetRtpPacketTransport() const {
return rtp_packet_transport_;
}
PacketTransportInterface* RtpTransport::GetRtcpPacketTransport() const {
return rtcp_packet_transport_;
}
RTCError RtpTransport::SetParameters(const RtpTransportParameters& parameters) {
if (parameters_.rtcp.mux && !parameters.rtcp.mux) {
LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_STATE,
@ -272,12 +252,7 @@ bool RtpTransport::WantsPacket(bool rtcp,
<< " packet: wrong size=" << packet->size();
return false;
}
if (rtcp) {
// Permit all (seemingly valid) RTCP packets.
return true;
}
// Check whether we handle this payload.
return HandlesPacket(packet->data(), packet->size());
return true;
}
} // namespace webrtc

View File

@ -13,7 +13,7 @@
#include <string>
#include "pc/bundlefilter.h"
#include "api/ortc/rtptransportinterface.h"
#include "pc/rtptransportinternal.h"
#include "rtc_base/sigslot.h"
@ -49,13 +49,19 @@ class RtpTransport : public RtpTransportInternal {
}
void SetRtcpPacketTransport(rtc::PacketTransportInternal* rtcp) override;
PacketTransportInterface* GetRtpPacketTransport() const override;
PacketTransportInterface* GetRtcpPacketTransport() const override;
PacketTransportInterface* GetRtpPacketTransport() const override {
return rtp_packet_transport_;
}
PacketTransportInterface* GetRtcpPacketTransport() const override {
return rtcp_packet_transport_;
}
// TODO(zstein): Use these RtcpParameters for configuration elsewhere.
RTCError SetParameters(const RtpTransportParameters& parameters) override;
RtpTransportParameters GetParameters() const override;
bool IsReadyToSend() const override { return ready_to_send_; }
bool IsWritable(bool rtcp) const override;
bool SendRtpPacket(rtc::CopyOnWriteBuffer* packet,
@ -66,9 +72,7 @@ class RtpTransport : public RtpTransportInternal {
const rtc::PacketOptions& options,
int flags) override;
bool HandlesPayloadType(int payload_type) const override;
void AddHandledPayloadType(int payload_type) override;
bool IsSrtpActive() const override { return false; }
void SetMetricsObserver(
rtc::scoped_refptr<MetricsObserverInterface> metrics_observer) override {}
@ -106,6 +110,15 @@ class RtpTransport : public RtpTransportInternal {
bool WantsPacket(bool rtcp, const rtc::CopyOnWriteBuffer* packet);
RTCError SetSrtpSendKey(const cricket::CryptoParams& params) override {
RTC_NOTREACHED();
return RTCError::OK();
}
RTCError SetSrtpReceiveKey(const cricket::CryptoParams& params) override {
RTC_NOTREACHED();
return RTCError::OK();
}
bool rtcp_mux_enabled_;
rtc::PacketTransportInternal* rtp_packet_transport_ = nullptr;
@ -116,8 +129,6 @@ class RtpTransport : public RtpTransportInternal {
bool rtcp_ready_to_send_ = false;
RtpTransportParameters parameters_;
cricket::BundleFilter bundle_filter_;
};
} // namespace webrtc

View File

@ -60,9 +60,19 @@ TEST(RtpTransportTest, SetRtpTransportKeepAliveNotSupported) {
class SignalObserver : public sigslot::has_slots<> {
public:
explicit SignalObserver(RtpTransport* transport) {
transport_ = transport;
transport->SignalReadyToSend.connect(this, &SignalObserver::OnReadyToSend);
transport->SignalNetworkRouteChanged.connect(
this, &SignalObserver::OnNetworkRouteChanged);
if (transport->rtp_packet_transport()) {
transport->rtp_packet_transport()->SignalSentPacket.connect(
this, &SignalObserver::OnSentPacket);
}
if (transport->rtcp_packet_transport()) {
transport->rtcp_packet_transport()->SignalSentPacket.connect(
this, &SignalObserver::OnSentPacket);
}
}
bool ready() const { return ready_; }
@ -73,7 +83,24 @@ class SignalObserver : public sigslot::has_slots<> {
network_route_ = std::move(network_route);
}
void OnSentPacket(rtc::PacketTransportInternal* packet_transport,
const rtc::SentPacket& sent_packet) {
if (packet_transport == transport_->rtp_packet_transport()) {
rtp_transport_sent_count_++;
} else {
ASSERT_EQ(transport_->rtcp_packet_transport(), packet_transport);
rtcp_transport_sent_count_++;
}
}
int rtp_transport_sent_count() { return rtp_transport_sent_count_; }
int rtcp_transport_sent_count() { return rtcp_transport_sent_count_; }
private:
int rtp_transport_sent_count_ = 0;
int rtcp_transport_sent_count_ = 0;
RtpTransport* transport_ = nullptr;
bool ready_ = false;
rtc::Optional<rtc::NetworkRoute> network_route_;
};
@ -197,6 +224,32 @@ TEST(RtpTransportTest, SetRtcpTransportWithNetworkRouteChanged) {
EXPECT_FALSE(observer.network_route());
}
// Test that RTCP packets are sent over correct transport based on the RTCP-mux
// status.
TEST(RtpTransportTest, RtcpPacketSentOverCorrectTransport) {
// If the RTCP-mux is not enabled, RTCP packets are expected to be sent over
// the RtcpPacketTransport.
RtpTransport transport(kMuxDisabled);
rtc::FakePacketTransport fake_rtcp("fake_rtcp");
rtc::FakePacketTransport fake_rtp("fake_rtp");
transport.SetRtcpPacketTransport(&fake_rtcp); // rtcp ready
transport.SetRtpPacketTransport(&fake_rtp); // rtp ready
SignalObserver observer(&transport);
fake_rtp.SetDestination(&fake_rtp, true);
fake_rtcp.SetDestination(&fake_rtcp, true);
rtc::CopyOnWriteBuffer packet;
EXPECT_TRUE(transport.SendRtcpPacket(&packet, rtc::PacketOptions(), 0));
EXPECT_EQ(1, observer.rtcp_transport_sent_count());
// The RTCP packets are expected to be sent over RtpPacketTransport if
// RTCP-mux is enabled.
transport.SetRtcpMuxEnabled(true);
EXPECT_TRUE(transport.SendRtcpPacket(&packet, rtc::PacketOptions(), 0));
EXPECT_EQ(1, observer.rtp_transport_sent_count());
}
class SignalCounter : public sigslot::has_slots<> {
public:
explicit SignalCounter(RtpTransport* transport) {
@ -263,7 +316,6 @@ TEST(RtpTransportTest, SignalHandledRtpPayloadType) {
rtc::FakePacketTransport fake_rtp("fake_rtp");
fake_rtp.SetDestination(&fake_rtp, true);
transport.SetRtpPacketTransport(&fake_rtp);
transport.AddHandledPayloadType(0x11);
// An rtp packet.
const rtc::PacketOptions options;
@ -274,21 +326,4 @@ TEST(RtpTransportTest, SignalHandledRtpPayloadType) {
EXPECT_EQ(0, observer.rtcp_count());
}
// Test that SignalPacketReceived does not fire when a RTP packet with an
// unhandled payload type is received.
TEST(RtpTransportTest, DontSignalUnhandledRtpPayloadType) {
RtpTransport transport(kMuxDisabled);
SignalPacketReceivedCounter observer(&transport);
rtc::FakePacketTransport fake_rtp("fake_rtp");
fake_rtp.SetDestination(&fake_rtp, true);
transport.SetRtpPacketTransport(&fake_rtp);
const rtc::PacketOptions options;
const int flags = 0;
rtc::Buffer rtp_data(kRtpData, kRtpLen);
fake_rtp.SendPacket(rtp_data.data<char>(), kRtpLen, options, flags);
EXPECT_EQ(0, observer.rtp_count());
EXPECT_EQ(0, observer.rtcp_count());
}
} // namespace webrtc

View File

@ -13,11 +13,12 @@
#include <string>
#include "api/ortc/rtptransportinterface.h"
#include "api/ortc/srtptransportinterface.h"
#include "api/umametrics.h"
#include "p2p/base/icetransportinternal.h"
#include "rtc_base/networkroute.h"
#include "rtc_base/sigslot.h"
#include "rtc_base/sslstreamadapter.h"
namespace rtc {
class CopyOnWriteBuffer;
@ -27,11 +28,11 @@ struct PacketTime;
namespace webrtc {
// This represents the internal interface beneath RtpTransportInterface;
// This represents the internal interface beneath SrtpTransportInterface;
// it is not accessible to API consumers but is accessible to internal classes
// in order to send and receive RTP and RTCP packets belonging to a single RTP
// session. Additional convenience and configuration methods are also provided.
class RtpTransportInternal : public RtpTransportInterface,
class RtpTransportInternal : public SrtpTransportInterface,
public sigslot::has_slots<> {
public:
virtual void SetRtcpMuxEnabled(bool enable) = 0;
@ -47,6 +48,8 @@ class RtpTransportInternal : public RtpTransportInterface,
virtual rtc::PacketTransportInternal* rtcp_packet_transport() const = 0;
virtual void SetRtcpPacketTransport(rtc::PacketTransportInternal* rtcp) = 0;
virtual bool IsReadyToSend() const = 0;
// Called whenever a transport's ready-to-send state changes. The argument
// is true if all used transports are ready to send. This is more specific
// than just "writable"; it means the last send didn't return ENOTCONN.
@ -80,9 +83,7 @@ class RtpTransportInternal : public RtpTransportInterface,
const rtc::PacketOptions& options,
int flags) = 0;
virtual bool HandlesPayloadType(int payload_type) const = 0;
virtual void AddHandledPayloadType(int payload_type) = 0;
virtual bool IsSrtpActive() const = 0;
virtual void SetMetricsObserver(
rtc::scoped_refptr<MetricsObserverInterface> metrics_observer) = 0;

View File

@ -24,6 +24,8 @@ namespace webrtc {
// methods.
class RtpTransportInternalAdapter : public RtpTransportInternal {
public:
RtpTransportInternalAdapter() {}
explicit RtpTransportInternalAdapter(RtpTransportInternal* transport)
: transport_(transport) {
RTC_DCHECK(transport_);
@ -51,6 +53,8 @@ class RtpTransportInternalAdapter : public RtpTransportInternal {
transport_->SetRtcpPacketTransport(rtcp);
}
bool IsReadyToSend() const override { return transport_->IsReadyToSend(); }
bool IsWritable(bool rtcp) const override {
return transport_->IsWritable(rtcp);
}
@ -67,14 +71,6 @@ class RtpTransportInternalAdapter : public RtpTransportInternal {
return transport_->SendRtcpPacket(packet, options, flags);
}
bool HandlesPayloadType(int payload_type) const override {
return transport_->HandlesPayloadType(payload_type);
}
void AddHandledPayloadType(int payload_type) override {
return transport_->AddHandledPayloadType(payload_type);
}
// RtpTransportInterface overrides.
PacketTransportInterface* GetRtpPacketTransport() const override {
return transport_->GetRtpPacketTransport();
@ -92,6 +88,8 @@ class RtpTransportInternalAdapter : public RtpTransportInternal {
return transport_->GetParameters();
}
RtpTransportAdapter* GetInternal() override { return nullptr; }
void SetMetricsObserver(
rtc::scoped_refptr<MetricsObserverInterface> metrics_observer) override {
transport_->SetMetricsObserver(metrics_observer);
@ -99,7 +97,7 @@ class RtpTransportInternalAdapter : public RtpTransportInternal {
protected:
// Owned by the subclasses.
RtpTransportInternal* transport_;
RtpTransportInternal* transport_ = nullptr;
};
} // namespace webrtc

View File

@ -21,19 +21,19 @@
#include "rtc_base/copyonwritebuffer.h"
#include "rtc_base/ptr_util.h"
#include "rtc_base/trace_event.h"
#include "rtc_base/zero_memory.h"
namespace webrtc {
SrtpTransport::SrtpTransport(bool rtcp_mux_enabled)
: RtpTransportInternalAdapter(new RtpTransport(rtcp_mux_enabled)) {
// Own the raw pointer |transport| from the base class.
rtp_transport_.reset(transport_);
rtp_transport_.reset(static_cast<RtpTransport*>(transport_));
RTC_DCHECK(rtp_transport_);
ConnectToRtpTransport();
}
SrtpTransport::SrtpTransport(
std::unique_ptr<RtpTransportInternal> rtp_transport)
SrtpTransport::SrtpTransport(std::unique_ptr<RtpTransport> rtp_transport)
: RtpTransportInternalAdapter(rtp_transport.get()),
rtp_transport_(std::move(rtp_transport)) {
RTC_DCHECK(rtp_transport_);
@ -52,6 +52,86 @@ void SrtpTransport::ConnectToRtpTransport() {
rtp_transport_->SignalSentPacket.connect(this, &SrtpTransport::OnSentPacket);
}
RTCError SrtpTransport::SetSrtpSendKey(const cricket::CryptoParams& params) {
if (send_params_) {
LOG_AND_RETURN_ERROR(
webrtc::RTCErrorType::UNSUPPORTED_OPERATION,
"Setting the SRTP send key twice is currently unsupported.");
}
if (recv_params_ && recv_params_->cipher_suite != params.cipher_suite) {
LOG_AND_RETURN_ERROR(
webrtc::RTCErrorType::UNSUPPORTED_OPERATION,
"The send key and receive key must have the same cipher suite.");
}
send_cipher_suite_ = rtc::SrtpCryptoSuiteFromName(params.cipher_suite);
if (*send_cipher_suite_ == rtc::SRTP_INVALID_CRYPTO_SUITE) {
return RTCError(RTCErrorType::INVALID_PARAMETER,
"Invalid SRTP crypto suite");
}
int send_key_len, send_salt_len;
if (!rtc::GetSrtpKeyAndSaltLengths(*send_cipher_suite_, &send_key_len,
&send_salt_len)) {
return RTCError(RTCErrorType::INVALID_PARAMETER,
"Could not get lengths for crypto suite(s):"
" send cipher_suite ");
}
send_key_ = rtc::ZeroOnFreeBuffer<uint8_t>(send_key_len + send_salt_len);
if (!ParseKeyParams(params.key_params, send_key_.data(), send_key_.size())) {
return RTCError(RTCErrorType::INVALID_PARAMETER,
"Failed to parse the crypto key params");
}
if (!MaybeSetKeyParams()) {
return RTCError(RTCErrorType::INVALID_PARAMETER,
"Failed to set the crypto key params");
}
send_params_ = params;
return RTCError::OK();
}
RTCError SrtpTransport::SetSrtpReceiveKey(const cricket::CryptoParams& params) {
if (recv_params_) {
LOG_AND_RETURN_ERROR(
webrtc::RTCErrorType::UNSUPPORTED_OPERATION,
"Setting the SRTP send key twice is currently unsupported.");
}
if (send_params_ && send_params_->cipher_suite != params.cipher_suite) {
LOG_AND_RETURN_ERROR(
webrtc::RTCErrorType::UNSUPPORTED_OPERATION,
"The send key and receive key must have the same cipher suite.");
}
recv_cipher_suite_ = rtc::SrtpCryptoSuiteFromName(params.cipher_suite);
if (*recv_cipher_suite_ == rtc::SRTP_INVALID_CRYPTO_SUITE) {
return RTCError(RTCErrorType::INVALID_PARAMETER,
"Invalid SRTP crypto suite");
}
int recv_key_len, recv_salt_len;
if (!rtc::GetSrtpKeyAndSaltLengths(*recv_cipher_suite_, &recv_key_len,
&recv_salt_len)) {
return RTCError(RTCErrorType::INVALID_PARAMETER,
"Could not get lengths for crypto suite(s):"
" recv cipher_suite ");
}
recv_key_ = rtc::ZeroOnFreeBuffer<uint8_t>(recv_key_len + recv_salt_len);
if (!ParseKeyParams(params.key_params, recv_key_.data(), recv_key_.size())) {
return RTCError(RTCErrorType::INVALID_PARAMETER,
"Failed to parse the crypto key params");
}
if (!MaybeSetKeyParams()) {
return RTCError(RTCErrorType::INVALID_PARAMETER,
"Failed to set the crypto key params");
}
recv_params_ = params;
return RTCError::OK();
}
bool SrtpTransport::SendRtpPacket(rtc::CopyOnWriteBuffer* packet,
const rtc::PacketOptions& options,
int flags) {
@ -68,7 +148,7 @@ bool SrtpTransport::SendPacket(bool rtcp,
rtc::CopyOnWriteBuffer* packet,
const rtc::PacketOptions& options,
int flags) {
if (!IsActive()) {
if (!IsSrtpActive()) {
RTC_LOG(LS_ERROR)
<< "Failed to send the packet because SRTP transport is inactive.";
return false;
@ -140,7 +220,7 @@ bool SrtpTransport::SendPacket(bool rtcp,
void SrtpTransport::OnPacketReceived(bool rtcp,
rtc::CopyOnWriteBuffer* packet,
const rtc::PacketTime& packet_time) {
if (!IsActive()) {
if (!IsSrtpActive()) {
RTC_LOG(LS_WARNING)
<< "Inactive SRTP transport received a packet. Drop it.";
return;
@ -181,7 +261,7 @@ void SrtpTransport::OnNetworkRouteChanged(
// Only append the SRTP overhead when there is a selected network route.
if (network_route) {
int srtp_overhead = 0;
if (IsActive()) {
if (IsSrtpActive()) {
GetSrtpOverhead(&srtp_overhead);
}
network_route->packet_overhead += srtp_overhead;
@ -271,7 +351,7 @@ bool SrtpTransport::SetRtcpParams(int send_cs,
return true;
}
bool SrtpTransport::IsActive() const {
bool SrtpTransport::IsSrtpActive() const {
return send_session_ && recv_session_;
}
@ -297,7 +377,7 @@ void SrtpTransport::CreateSrtpSessions() {
}
bool SrtpTransport::ProtectRtp(void* p, int in_len, int max_len, int* out_len) {
if (!IsActive()) {
if (!IsSrtpActive()) {
RTC_LOG(LS_WARNING) << "Failed to ProtectRtp: SRTP not active";
return false;
}
@ -310,7 +390,7 @@ bool SrtpTransport::ProtectRtp(void* p,
int max_len,
int* out_len,
int64_t* index) {
if (!IsActive()) {
if (!IsSrtpActive()) {
RTC_LOG(LS_WARNING) << "Failed to ProtectRtp: SRTP not active";
return false;
}
@ -322,7 +402,7 @@ bool SrtpTransport::ProtectRtcp(void* p,
int in_len,
int max_len,
int* out_len) {
if (!IsActive()) {
if (!IsSrtpActive()) {
RTC_LOG(LS_WARNING) << "Failed to ProtectRtcp: SRTP not active";
return false;
}
@ -335,7 +415,7 @@ bool SrtpTransport::ProtectRtcp(void* p,
}
bool SrtpTransport::UnprotectRtp(void* p, int in_len, int* out_len) {
if (!IsActive()) {
if (!IsSrtpActive()) {
RTC_LOG(LS_WARNING) << "Failed to UnprotectRtp: SRTP not active";
return false;
}
@ -344,7 +424,7 @@ bool SrtpTransport::UnprotectRtp(void* p, int in_len, int* out_len) {
}
bool SrtpTransport::UnprotectRtcp(void* p, int in_len, int* out_len) {
if (!IsActive()) {
if (!IsSrtpActive()) {
RTC_LOG(LS_WARNING) << "Failed to UnprotectRtcp: SRTP not active";
return false;
}
@ -359,7 +439,7 @@ bool SrtpTransport::UnprotectRtcp(void* p, int in_len, int* out_len) {
bool SrtpTransport::GetRtpAuthParams(uint8_t** key,
int* key_len,
int* tag_len) {
if (!IsActive()) {
if (!IsSrtpActive()) {
RTC_LOG(LS_WARNING) << "Failed to GetRtpAuthParams: SRTP not active";
return false;
}
@ -369,7 +449,7 @@ bool SrtpTransport::GetRtpAuthParams(uint8_t** key,
}
bool SrtpTransport::GetSrtpOverhead(int* srtp_overhead) const {
if (!IsActive()) {
if (!IsSrtpActive()) {
RTC_LOG(LS_WARNING) << "Failed to GetSrtpOverhead: SRTP not active";
return false;
}
@ -380,7 +460,7 @@ bool SrtpTransport::GetSrtpOverhead(int* srtp_overhead) const {
}
void SrtpTransport::EnableExternalAuth() {
RTC_DCHECK(!IsActive());
RTC_DCHECK(!IsSrtpActive());
external_auth_enabled_ = true;
}
@ -389,7 +469,7 @@ bool SrtpTransport::IsExternalAuthEnabled() const {
}
bool SrtpTransport::IsExternalAuthActive() const {
if (!IsActive()) {
if (!IsSrtpActive()) {
RTC_LOG(LS_WARNING)
<< "Failed to check IsExternalAuthActive: SRTP not active";
return false;
@ -399,6 +479,42 @@ bool SrtpTransport::IsExternalAuthActive() const {
return send_session_->IsExternalAuthActive();
}
bool SrtpTransport::MaybeSetKeyParams() {
if (!send_cipher_suite_ || !recv_cipher_suite_) {
return true;
}
return SetRtpParams(*send_cipher_suite_, send_key_.data(),
static_cast<int>(send_key_.size()), std::vector<int>(),
*recv_cipher_suite_, recv_key_.data(),
static_cast<int>(recv_key_.size()), std::vector<int>());
}
bool SrtpTransport::ParseKeyParams(const std::string& key_params,
uint8_t* key,
size_t len) {
// example key_params: "inline:YUJDZGVmZ2hpSktMbW9QUXJzVHVWd3l6MTIzNDU2"
// Fail if key-method is wrong.
if (key_params.find("inline:") != 0) {
return false;
}
// Fail if base64 decode fails, or the key is the wrong size.
std::string key_b64(key_params.substr(7)), key_str;
if (!rtc::Base64::Decode(key_b64, rtc::Base64::DO_STRICT, &key_str,
nullptr) ||
key_str.size() != len) {
return false;
}
memcpy(key, key_str.c_str(), len);
// TODO(bugs.webrtc.org/8905): Switch to ZeroOnFreeBuffer for storing
// sensitive data.
rtc::ExplicitZeroMemory(&key_str[0], key_str.size());
return true;
}
void SrtpTransport::SetMetricsObserver(
rtc::scoped_refptr<MetricsObserverInterface> metrics_observer) {
metrics_observer_ = metrics_observer;

View File

@ -16,10 +16,13 @@
#include <utility>
#include <vector>
#include "api/ortc/srtptransportinterface.h"
#include "p2p/base/dtlstransportinternal.h"
#include "p2p/base/icetransportinternal.h"
#include "pc/rtptransport.h"
#include "pc/rtptransportinternaladapter.h"
#include "pc/srtpfilter.h"
#include "pc/srtpsession.h"
#include "rtc_base/buffer.h"
#include "rtc_base/checks.h"
namespace webrtc {
@ -30,7 +33,29 @@ class SrtpTransport : public RtpTransportInternalAdapter {
public:
explicit SrtpTransport(bool rtcp_mux_enabled);
explicit SrtpTransport(std::unique_ptr<RtpTransportInternal> rtp_transport);
explicit SrtpTransport(std::unique_ptr<RtpTransport> rtp_transport);
virtual ~SrtpTransport() {}
// SrtpTransportInterface overrides.
PacketTransportInterface* GetRtpPacketTransport() const override {
return rtp_transport_->GetRtpPacketTransport();
}
PacketTransportInterface* GetRtcpPacketTransport() const override {
return rtp_transport_->GetRtcpPacketTransport();
}
// TODO(zstein): Use these RtcpParameters for configuration elsewhere.
RTCError SetParameters(const RtpTransportParameters& parameters) override {
return rtp_transport_->SetParameters(parameters);
}
RtpTransportParameters GetParameters() const override {
return rtp_transport_->GetParameters();
}
// SrtpTransportInterface specific implementation.
RTCError SetSrtpSendKey(const cricket::CryptoParams& params) override;
RTCError SetSrtpReceiveKey(const cricket::CryptoParams& params) override;
bool SendRtpPacket(rtc::CopyOnWriteBuffer* packet,
const rtc::PacketOptions& options,
@ -42,10 +67,7 @@ class SrtpTransport : public RtpTransportInternalAdapter {
// The transport becomes active if the send_session_ and recv_session_ are
// created.
bool IsActive() const;
// TODO(zstein): Remove this when we remove RtpTransportAdapter.
RtpTransportAdapter* GetInternal() override { return nullptr; }
bool IsSrtpActive() const override;
// Create new send/recv sessions and set the negotiated crypto keys for RTP
// packet encryption. The keys can either come from SDES negotiation or DTLS
@ -138,14 +160,24 @@ class SrtpTransport : public RtpTransportInternalAdapter {
bool UnprotectRtcp(void* data, int in_len, int* out_len);
bool MaybeSetKeyParams();
bool ParseKeyParams(const std::string& key_params, uint8_t* key, size_t len);
const std::string content_name_;
std::unique_ptr<RtpTransportInternal> rtp_transport_;
std::unique_ptr<RtpTransport> rtp_transport_;
std::unique_ptr<cricket::SrtpSession> send_session_;
std::unique_ptr<cricket::SrtpSession> recv_session_;
std::unique_ptr<cricket::SrtpSession> send_rtcp_session_;
std::unique_ptr<cricket::SrtpSession> recv_rtcp_session_;
rtc::Optional<cricket::CryptoParams> send_params_;
rtc::Optional<cricket::CryptoParams> recv_params_;
rtc::Optional<int> send_cipher_suite_;
rtc::Optional<int> recv_cipher_suite_;
rtc::ZeroOnFreeBuffer<uint8_t> send_key_;
rtc::ZeroOnFreeBuffer<uint8_t> recv_key_;
bool external_auth_enabled_ = false;
int rtp_abs_sendtime_extn_id_ = -1;

View File

@ -57,12 +57,6 @@ class SrtpTransportTest : public testing::Test, public sigslot::has_slots<> {
rtp_transport1->SetRtpPacketTransport(rtp_packet_transport1_.get());
rtp_transport2->SetRtpPacketTransport(rtp_packet_transport2_.get());
// Add payload type for RTP packet and RTCP packet.
rtp_transport1->AddHandledPayloadType(0x00);
rtp_transport2->AddHandledPayloadType(0x00);
rtp_transport1->AddHandledPayloadType(0xc9);
rtp_transport2->AddHandledPayloadType(0xc9);
srtp_transport1_ =
rtc::MakeUnique<SrtpTransport>(std::move(rtp_transport1));
srtp_transport2_ =
@ -229,8 +223,8 @@ class SrtpTransportTest : public testing::Test, public sigslot::has_slots<> {
cs, key1, key1_len, extension_ids, cs, key2, key2_len, extension_ids));
EXPECT_TRUE(srtp_transport2_->SetRtcpParams(
cs, key2, key2_len, extension_ids, cs, key1, key1_len, extension_ids));
EXPECT_TRUE(srtp_transport1_->IsActive());
EXPECT_TRUE(srtp_transport2_->IsActive());
EXPECT_TRUE(srtp_transport1_->IsSrtpActive());
EXPECT_TRUE(srtp_transport2_->IsSrtpActive());
if (rtc::IsGcmCryptoSuite(cs)) {
EXPECT_FALSE(srtp_transport1_->IsExternalAuthActive());
EXPECT_FALSE(srtp_transport2_->IsExternalAuthActive());
@ -315,8 +309,8 @@ class SrtpTransportTest : public testing::Test, public sigslot::has_slots<> {
EXPECT_TRUE(srtp_transport2_->SetRtpParams(cs, key2, key2_len,
encrypted_headers, cs, key1,
key1_len, encrypted_headers));
EXPECT_TRUE(srtp_transport1_->IsActive());
EXPECT_TRUE(srtp_transport2_->IsActive());
EXPECT_TRUE(srtp_transport1_->IsSrtpActive());
EXPECT_TRUE(srtp_transport2_->IsSrtpActive());
EXPECT_FALSE(srtp_transport1_->IsExternalAuthActive());
EXPECT_FALSE(srtp_transport2_->IsExternalAuthActive());
TestSendRecvPacketWithEncryptedHeaderExtension(cs_name, encrypted_headers);

View File

@ -124,8 +124,8 @@ class FakePeerConnectionForStats : public FakePeerConnectionBase {
auto* voice_media_channel_ptr = voice_media_channel.get();
voice_channel_ = rtc::MakeUnique<cricket::VoiceChannel>(
worker_thread_, network_thread_, signaling_thread_, nullptr,
std::move(voice_media_channel), mid, kDefaultRtcpMuxRequired,
kDefaultSrtpRequired);
std::move(voice_media_channel), mid, kDefaultSrtpRequired,
rtc::CryptoOptions());
voice_channel_->set_transport_name_for_testing(transport_name);
GetOrCreateFirstTransceiverOfType(cricket::MEDIA_TYPE_AUDIO)
->internal()
@ -141,8 +141,8 @@ class FakePeerConnectionForStats : public FakePeerConnectionBase {
auto video_media_channel_ptr = video_media_channel.get();
video_channel_ = rtc::MakeUnique<cricket::VideoChannel>(
worker_thread_, network_thread_, signaling_thread_,
std::move(video_media_channel), mid, kDefaultRtcpMuxRequired,
kDefaultSrtpRequired);
std::move(video_media_channel), mid, kDefaultSrtpRequired,
rtc::CryptoOptions());
video_channel_->set_transport_name_for_testing(transport_name);
GetOrCreateFirstTransceiverOfType(cricket::MEDIA_TYPE_VIDEO)
->internal()

View File

@ -1,159 +0,0 @@
/*
* Copyright 2009 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.
*/
#ifndef PC_TEST_FAKETRANSPORTCONTROLLER_H_
#define PC_TEST_FAKETRANSPORTCONTROLLER_H_
#include <memory>
#include <string>
#include <vector>
#include "p2p/base/fakedtlstransport.h"
#include "p2p/base/fakeicetransport.h"
#include "pc/transportcontroller.h"
#include "rtc_base/bind.h"
#include "rtc_base/sslfingerprint.h"
#include "rtc_base/thread.h"
namespace cricket {
// Fake TransportController class, which can be passed into a WebRtcSession
// object for test purposes. Can be connected to other FakeTransportControllers
// via Connect().
//
// This fake is unusual in that for the most part, it's implemented with the
// real TransportController code, but with fake TransportChannels underneath.
class FakeTransportController : public TransportController {
public:
FakeTransportController()
: TransportController(rtc::Thread::Current(),
rtc::Thread::Current(),
nullptr,
/*redetermine_role_on_ice_restart=*/true,
rtc::CryptoOptions()) {}
explicit FakeTransportController(bool redetermine_role_on_ice_restart)
: TransportController(rtc::Thread::Current(),
rtc::Thread::Current(),
nullptr,
redetermine_role_on_ice_restart,
rtc::CryptoOptions()) {}
explicit FakeTransportController(IceRole role)
: TransportController(rtc::Thread::Current(),
rtc::Thread::Current(),
nullptr,
/*redetermine_role_on_ice_restart=*/true,
rtc::CryptoOptions()) {
SetIceRole(role);
}
explicit FakeTransportController(rtc::Thread* network_thread)
: TransportController(rtc::Thread::Current(),
network_thread,
nullptr,
/*redetermine_role_on_ice_restart=*/true,
rtc::CryptoOptions()) {}
FakeTransportController(rtc::Thread* network_thread, IceRole role)
: TransportController(rtc::Thread::Current(),
network_thread,
nullptr,
/*redetermine_role_on_ice_restart=*/true,
rtc::CryptoOptions()) {
SetIceRole(role);
}
FakeDtlsTransport* GetFakeDtlsTransport_n(const std::string& transport_name,
int component) {
return static_cast<FakeDtlsTransport*>(
get_channel_for_testing(transport_name, component));
}
// Simulate the exchange of transport descriptions, and the gathering and
// exchange of ICE candidates.
void Connect(FakeTransportController* dest) {
for (const std::string& transport_name : transport_names_for_testing()) {
std::unique_ptr<rtc::SSLFingerprint> local_fingerprint;
std::unique_ptr<rtc::SSLFingerprint> remote_fingerprint;
if (certificate_for_testing()) {
local_fingerprint.reset(rtc::SSLFingerprint::CreateFromCertificate(
certificate_for_testing()));
}
if (dest->certificate_for_testing()) {
remote_fingerprint.reset(rtc::SSLFingerprint::CreateFromCertificate(
dest->certificate_for_testing()));
}
TransportDescription local_desc(
std::vector<std::string>(),
rtc::CreateRandomString(cricket::ICE_UFRAG_LENGTH),
rtc::CreateRandomString(cricket::ICE_PWD_LENGTH),
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_NONE,
local_fingerprint.get());
TransportDescription remote_desc(
std::vector<std::string>(),
rtc::CreateRandomString(cricket::ICE_UFRAG_LENGTH),
rtc::CreateRandomString(cricket::ICE_PWD_LENGTH),
cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_NONE,
remote_fingerprint.get());
std::string err;
SetLocalTransportDescription(transport_name, local_desc,
webrtc::SdpType::kOffer, &err);
dest->SetRemoteTransportDescription(transport_name, local_desc,
webrtc::SdpType::kOffer, &err);
dest->SetLocalTransportDescription(transport_name, remote_desc,
webrtc::SdpType::kAnswer, &err);
SetRemoteTransportDescription(transport_name, remote_desc,
webrtc::SdpType::kAnswer, &err);
}
MaybeStartGathering();
dest->MaybeStartGathering();
network_thread()->Invoke<void>(
RTC_FROM_HERE,
rtc::Bind(&FakeTransportController::SetChannelDestinations_n, this,
dest));
}
void DestroyRtcpTransport(const std::string& transport_name) {
DestroyDtlsTransport_n(transport_name,
cricket::ICE_CANDIDATE_COMPONENT_RTCP);
}
protected:
IceTransportInternal* CreateIceTransportChannel_n(
const std::string& transport_name,
int component) override {
return new FakeIceTransport(transport_name, component);
}
DtlsTransportInternal* CreateDtlsTransportChannel_n(
const std::string& transport_name,
int component,
IceTransportInternal* ice) override {
return new FakeDtlsTransport(static_cast<FakeIceTransport*>(ice));
}
private:
void SetChannelDestinations_n(FakeTransportController* dest) {
for (DtlsTransportInternal* tc : channels_for_testing()) {
FakeDtlsTransport* local = static_cast<FakeDtlsTransport*>(tc);
FakeDtlsTransport* remote = dest->GetFakeDtlsTransport_n(
local->transport_name(), local->component());
if (remote) {
bool asymmetric = false;
local->SetDestination(remote, asymmetric);
}
}
}
};
} // namespace cricket
#endif // PC_TEST_FAKETRANSPORTCONTROLLER_H_

View File

@ -61,7 +61,7 @@ static const char kFireFoxSdpOffer[] =
"a=candidate:5 2 UDP 1694302206 74.95.2.170 33611 typ srflx raddr"
" 10.0.254.2 rport 58890\r\n"
#ifdef HAVE_SCTP
"m=application 45536 SCTP/DTLS 5000\r\n"
"m=application 45536 DTLS/SCTP 5000\r\n"
"c=IN IP4 74.95.2.170\r\n"
"a=fmtp:5000 protocol=webrtc-datachannel;streams=16\r\n"
"a=sendrecv\r\n"

File diff suppressed because it is too large Load Diff

View File

@ -1,315 +0,0 @@
/*
* Copyright 2015 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.
*/
#ifndef PC_TRANSPORTCONTROLLER_H_
#define PC_TRANSPORTCONTROLLER_H_
#include <map>
#include <memory>
#include <string>
#include <vector>
#include "api/candidate.h"
#include "p2p/base/dtlstransport.h"
#include "p2p/base/p2ptransportchannel.h"
#include "pc/dtlssrtptransport.h"
#include "pc/jseptransport.h"
#include "pc/rtptransport.h"
#include "pc/srtptransport.h"
#include "rtc_base/asyncinvoker.h"
#include "rtc_base/constructormagic.h"
#include "rtc_base/refcountedobject.h"
#include "rtc_base/sigslot.h"
#include "rtc_base/sslstreamadapter.h"
namespace rtc {
class Thread;
class PacketTransportInternal;
} // namespace rtc
namespace webrtc {
class MetricsObserverInterface;
class RtcEventLog;
} // namespace webrtc
namespace cricket {
class TransportController : public sigslot::has_slots<>,
public rtc::MessageHandler {
public:
// If |redetermine_role_on_ice_restart| is true, ICE role is redetermined
// upon setting a local transport description that indicates an ICE restart.
// For the constructor that doesn't take this parameter, it defaults to true.
//
// |crypto_options| is used to determine if created DTLS transports negotiate
// GCM crypto suites or not.
TransportController(rtc::Thread* signaling_thread,
rtc::Thread* network_thread,
PortAllocator* port_allocator,
bool redetermine_role_on_ice_restart,
const rtc::CryptoOptions& crypto_options,
webrtc::RtcEventLog* event_log = nullptr);
virtual ~TransportController();
rtc::Thread* signaling_thread() const { return signaling_thread_; }
rtc::Thread* network_thread() const { return network_thread_; }
PortAllocator* port_allocator() const { return port_allocator_; }
// Can only be set before transports are created.
// TODO(deadbeef): Make this an argument to the constructor once BaseSession
// and WebRtcSession are combined
bool SetSslMaxProtocolVersion(rtc::SSLProtocolVersion version);
void SetIceConfig(const IceConfig& config);
void SetIceRole(IceRole ice_role);
// Set the "needs-ice-restart" flag as described in JSEP. After the flag is
// set, offers should generate new ufrags/passwords until an ICE restart
// occurs.
void SetNeedsIceRestartFlag();
// Returns true if the ICE restart flag above was set, and no ICE restart has
// occurred yet for this transport (by applying a local description with
// changed ufrag/password). If the transport has been deleted as a result of
// bundling, returns false.
bool NeedsIceRestart(const std::string& transport_name) const;
bool GetSslRole(const std::string& transport_name, rtc::SSLRole* role) const;
// Specifies the identity to use in this session.
// Can only be called once.
bool SetLocalCertificate(
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate);
bool GetLocalCertificate(
const std::string& transport_name,
rtc::scoped_refptr<rtc::RTCCertificate>* certificate) const;
// Caller owns returned certificate chain. This method mainly exists for
// stats reporting.
std::unique_ptr<rtc::SSLCertChain> GetRemoteSSLCertChain(
const std::string& transport_name) const;
bool SetLocalTransportDescription(const std::string& transport_name,
const TransportDescription& tdesc,
webrtc::SdpType type,
std::string* err);
bool SetRemoteTransportDescription(const std::string& transport_name,
const TransportDescription& tdesc,
webrtc::SdpType type,
std::string* err);
// Start gathering candidates for any new transports, or transports doing an
// ICE restart.
void MaybeStartGathering();
bool AddRemoteCandidates(const std::string& transport_name,
const Candidates& candidates,
std::string* err);
bool RemoveRemoteCandidates(const Candidates& candidates, std::string* err);
bool ReadyForRemoteCandidates(const std::string& transport_name) const;
// TODO(deadbeef): GetStats isn't const because all the way down to
// OpenSSLStreamAdapter,
// GetSslCipherSuite and GetDtlsSrtpCryptoSuite are not const. Fix this.
bool GetStats(const std::string& transport_name, TransportStats* stats);
void SetMetricsObserver(webrtc::MetricsObserverInterface* metrics_observer);
// Creates a channel if it doesn't exist. Otherwise, increments a reference
// count and returns an existing channel.
DtlsTransportInternal* CreateDtlsTransport(const std::string& transport_name,
int component);
virtual DtlsTransportInternal* CreateDtlsTransport_n(
const std::string& transport_name,
int component);
// Decrements a channel's reference count, and destroys the channel if
// nothing is referencing it.
virtual void DestroyDtlsTransport(const std::string& transport_name,
int component);
virtual void DestroyDtlsTransport_n(const std::string& transport_name,
int component);
// Create an SrtpTransport/DtlsSrtpTransport if it doesn't exist.
// Otherwise, increments a reference count and returns the existing one.
// These methods are not currently used but the plan is to transition
// PeerConnection and BaseChannel to use them instead of CreateDtlsTransport.
webrtc::SrtpTransport* CreateSdesTransport(const std::string& transport_name,
bool rtcp_mux_enabled);
webrtc::DtlsSrtpTransport* CreateDtlsSrtpTransport(
const std::string& transport_name,
bool rtcp_mux_enabled);
// Destroy an RTP level transport which can be an RtpTransport, an
// SrtpTransport or a DtlsSrtpTransport.
void DestroyTransport(const std::string& transport_name);
// TODO(deadbeef): Remove all for_testing methods!
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate_for_testing()
const {
return certificate_;
}
std::vector<std::string> transport_names_for_testing();
std::vector<DtlsTransportInternal*> channels_for_testing();
DtlsTransportInternal* get_channel_for_testing(
const std::string& transport_name,
int component);
// All of these signals are fired on the signalling thread.
// If any transport failed => failed,
// Else if all completed => completed,
// Else if all connected => connected,
// Else => connecting
sigslot::signal1<IceConnectionState> SignalConnectionState;
// Receiving if any transport is receiving
sigslot::signal1<bool> SignalReceiving;
// If all transports done gathering => complete,
// Else if any are gathering => gathering,
// Else => new
sigslot::signal1<IceGatheringState> SignalGatheringState;
// (transport_name, candidates)
sigslot::signal2<const std::string&, const Candidates&>
SignalCandidatesGathered;
sigslot::signal1<const Candidates&> SignalCandidatesRemoved;
sigslot::signal1<rtc::SSLHandshakeError> SignalDtlsHandshakeError;
protected:
// TODO(deadbeef): Get rid of these virtual methods. Used by
// FakeTransportController currently, but FakeTransportController shouldn't
// even be functioning by subclassing TransportController.
virtual IceTransportInternal* CreateIceTransportChannel_n(
const std::string& transport_name,
int component);
virtual DtlsTransportInternal* CreateDtlsTransportChannel_n(
const std::string& transport_name,
int component,
IceTransportInternal* ice);
private:
void OnMessage(rtc::Message* pmsg) override;
class ChannelPair;
typedef rtc::RefCountedObject<ChannelPair> RefCountedChannel;
// Wrapper for RtpTransport that keeps a reference count.
// When using SDES, |srtp_transport| is non-null, |dtls_srtp_transport| is
// null and |rtp_transport.get()| == |srtp_transport|,
// When using DTLS-SRTP, |dtls_srtp_transport| is non-null, |srtp_transport|
// is null and |rtp_transport.get()| == |dtls_srtp_transport|,
// When using unencrypted RTP, only |rtp_transport| is non-null.
struct RtpTransportWrapper {
// |rtp_transport| is always non-null.
std::unique_ptr<webrtc::RtpTransportInternal> rtp_transport;
webrtc::SrtpTransport* srtp_transport = nullptr;
webrtc::DtlsSrtpTransport* dtls_srtp_transport = nullptr;
};
typedef rtc::RefCountedObject<RtpTransportWrapper> RefCountedRtpTransport;
const RefCountedRtpTransport* FindRtpTransport(
const std::string& transport_name);
// Helper functions to get a channel or transport, or iterator to it (in case
// it needs to be erased).
std::vector<RefCountedChannel*>::iterator GetChannelIterator_n(
const std::string& transport_name,
int component);
std::vector<RefCountedChannel*>::const_iterator GetChannelIterator_n(
const std::string& transport_name,
int component) const;
const JsepTransport* GetJsepTransport(
const std::string& transport_name) const;
JsepTransport* GetJsepTransport(const std::string& transport_name);
const RefCountedChannel* GetChannel_n(const std::string& transport_name,
int component) const;
RefCountedChannel* GetChannel_n(const std::string& transport_name,
int component);
JsepTransport* GetOrCreateJsepTransport(const std::string& transport_name);
void DestroyAllChannels_n();
bool SetSslMaxProtocolVersion_n(rtc::SSLProtocolVersion version);
void SetIceConfig_n(const IceConfig& config);
void SetIceRole_n(IceRole ice_role);
bool GetSslRole_n(const std::string& transport_name,
rtc::SSLRole* role) const;
bool SetLocalCertificate_n(
const rtc::scoped_refptr<rtc::RTCCertificate>& certificate);
bool GetLocalCertificate_n(
const std::string& transport_name,
rtc::scoped_refptr<rtc::RTCCertificate>* certificate) const;
bool SetLocalTransportDescription_n(const std::string& transport_name,
const TransportDescription& tdesc,
webrtc::SdpType type,
std::string* err);
bool SetRemoteTransportDescription_n(const std::string& transport_name,
const TransportDescription& tdesc,
webrtc::SdpType type,
std::string* err);
void MaybeStartGathering_n();
bool AddRemoteCandidates_n(const std::string& transport_name,
const Candidates& candidates,
std::string* err);
bool RemoveRemoteCandidates_n(const Candidates& candidates, std::string* err);
bool ReadyForRemoteCandidates_n(const std::string& transport_name) const;
bool GetStats_n(const std::string& transport_name, TransportStats* stats);
void SetMetricsObserver_n(webrtc::MetricsObserverInterface* metrics_observer);
// Handlers for signals from Transport.
void OnChannelWritableState_n(rtc::PacketTransportInternal* transport);
void OnChannelReceivingState_n(rtc::PacketTransportInternal* transport);
void OnChannelGatheringState_n(IceTransportInternal* channel);
void OnChannelCandidateGathered_n(IceTransportInternal* channel,
const Candidate& candidate);
void OnChannelCandidatesRemoved(const Candidates& candidates);
void OnChannelCandidatesRemoved_n(IceTransportInternal* channel,
const Candidates& candidates);
void OnChannelRoleConflict_n(IceTransportInternal* channel);
void OnChannelStateChanged_n(IceTransportInternal* channel);
void UpdateAggregateStates_n();
void OnDtlsHandshakeError(rtc::SSLHandshakeError error);
rtc::Thread* const signaling_thread_ = nullptr;
rtc::Thread* const network_thread_ = nullptr;
PortAllocator* const port_allocator_ = nullptr;
std::map<std::string, std::unique_ptr<JsepTransport>> transports_;
std::vector<RefCountedChannel*> channels_;
std::map<std::string, RefCountedRtpTransport*> rtp_transports_;
// Aggregate state for TransportChannelImpls.
IceConnectionState connection_state_ = kIceConnectionConnecting;
bool receiving_ = false;
IceGatheringState gathering_state_ = kIceGatheringNew;
IceConfig ice_config_;
IceRole ice_role_ = ICEROLE_CONTROLLING;
bool redetermine_role_on_ice_restart_;
uint64_t ice_tiebreaker_ = rtc::CreateRandomId64();
rtc::CryptoOptions crypto_options_;
rtc::SSLProtocolVersion ssl_max_version_ = rtc::SSL_PROTOCOL_DTLS_12;
rtc::scoped_refptr<rtc::RTCCertificate> certificate_;
rtc::AsyncInvoker invoker_;
webrtc::MetricsObserverInterface* metrics_observer_ = nullptr;
webrtc::RtcEventLog* event_log_;
RTC_DISALLOW_COPY_AND_ASSIGN(TransportController);
};
} // namespace cricket
#endif // PC_TRANSPORTCONTROLLER_H_

File diff suppressed because it is too large Load Diff