/* * 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.
*/
using ::cricket::DtlsTransportInternal; using ::cricket::FakeVoiceMediaReceiveChannel; using ::cricket::FakeVoiceMediaSendChannel; using ::cricket::RidDescription; using ::cricket::RidDirection; using ::cricket::StreamParams; using ::testing::AllOf; using ::testing::ElementsAre; using ::testing::Field; using ::webrtc::RtpTransceiverDirection; using ::webrtc::SdpType;
template <class ChannelT, class MediaSendChannelT, class MediaReceiveChannelT, class MediaSendChannelInterfaceT, class MediaReceiveChannelInterfaceT, class ContentT, class MediaInfoT, class OptionsT> class Traits { public: typedef ChannelT Channel; typedef MediaSendChannelT MediaSendChannel; typedef MediaReceiveChannelT MediaReceiveChannel; typedef MediaSendChannelInterfaceT MediaSendChannelInterface; typedef MediaReceiveChannelInterfaceT MediaReceiveChannelInterface; typedef ContentT Content; typedef MediaInfoT MediaInfo; typedef OptionsT Options;
};
class VoiceTraits : public Traits<cricket::VoiceChannel,
cricket::FakeVoiceMediaSendChannel,
cricket::FakeVoiceMediaReceiveChannel,
cricket::VoiceMediaSendChannelInterface,
cricket::VoiceMediaReceiveChannelInterface,
cricket::AudioContentDescription,
cricket::VoiceMediaInfo,
cricket::AudioOptions> {};
class VideoTraits : public Traits<cricket::VideoChannel,
cricket::FakeVideoMediaSendChannel,
cricket::FakeVideoMediaReceiveChannel,
cricket::VideoMediaSendChannelInterface,
cricket::VideoMediaReceiveChannelInterface,
cricket::VideoContentDescription,
cricket::VideoMediaInfo,
cricket::VideoOptions> {};
// Base class for Voice/Video tests template <class T> class ChannelTest : public ::testing::Test, public sigslot::has_slots<> { public: enum Flags {
RTCP_MUX = 0x1,
SSRC_MUX = 0x8,
DTLS = 0x10, // Use BaseChannel with PacketTransportInternal rather than // DtlsTransportInternal.
RAW_PACKET_TRANSPORT = 0x20,
};
// Network thread is started in CreateChannels, to allow the test to // configure a fake clock before any threads are spawned and attempt to // access the time. if (network_thread_keeper_) {
network_thread_keeper_->Start();
}
// Make sure if using raw packet transports, they're used for both // channels.
RTC_DCHECK_EQ(flags1 & RAW_PACKET_TRANSPORT, flags2 & RAW_PACKET_TRANSPORT);
rtc::Thread* worker_thread = rtc::Thread::Current();
network_thread_->BlockingCall([&] { // Based on flags, create fake DTLS or raw packet transports.
if (flags1 & RAW_PACKET_TRANSPORT) {
fake_rtp_packet_transport1_.reset( new rtc::FakePacketTransport("channel1_rtp")); if (!(flags1 & RTCP_MUX)) {
fake_rtcp_packet_transport1_.reset( new rtc::FakePacketTransport("channel1_rtcp"));
}
} else { // Confirmed to work with KT_RSA and KT_ECDSA.
fake_rtp_dtls_transport1_.reset(new cricket::FakeDtlsTransport( "channel1", cricket::ICE_CANDIDATE_COMPONENT_RTP, network_thread_)); if (!(flags1 & RTCP_MUX)) {
fake_rtcp_dtls_transport1_.reset(new cricket::FakeDtlsTransport( "channel1", cricket::ICE_CANDIDATE_COMPONENT_RTCP,
network_thread_));
} if (flags1 & DTLS) { auto cert1 = rtc::RTCCertificate::Create(
rtc::SSLIdentity::Create("session1", rtc::KT_DEFAULT));
fake_rtp_dtls_transport1_->SetLocalCertificate(cert1); if (fake_rtcp_dtls_transport1_) {
fake_rtcp_dtls_transport1_->SetLocalCertificate(cert1);
}
}
} // Based on flags, create fake DTLS or raw packet transports. if (flags2 & RAW_PACKET_TRANSPORT) {
fake_rtp_packet_transport2_.reset( new rtc::FakePacketTransport("channel2_rtp")); if (!(flags2 & RTCP_MUX)) {
fake_rtcp_packet_transport2_.reset( new rtc::FakePacketTransport("channel2_rtcp"));
}
} else { // Confirmed to work with KT_RSA and KT_ECDSA.
fake_rtp_dtls_transport2_.reset(new cricket::FakeDtlsTransport( "channel2", cricket::ICE_CANDIDATE_COMPONENT_RTP, network_thread_)); if (!(flags2 & RTCP_MUX)) {
fake_rtcp_dtls_transport2_.reset(new cricket::FakeDtlsTransport( "channel2", cricket::ICE_CANDIDATE_COMPONENT_RTCP,
network_thread_));
} if (flags2 & DTLS) { auto cert2 = rtc::RTCCertificate::Create(
rtc::SSLIdentity::Create("session2", rtc::KT_DEFAULT));
fake_rtp_dtls_transport2_->SetLocalCertificate(cert2); if (fake_rtcp_dtls_transport2_) {
fake_rtcp_dtls_transport2_->SetLocalCertificate(cert2);
}
}
}
rtp_transport1_ = CreateRtpTransportBasedOnFlags(
fake_rtp_packet_transport1_.get(), fake_rtcp_packet_transport1_.get(),
fake_rtp_dtls_transport1_.get(), fake_rtcp_dtls_transport1_.get(),
flags1);
rtp_transport2_ = CreateRtpTransportBasedOnFlags(
fake_rtp_packet_transport2_.get(), fake_rtcp_packet_transport2_.get(),
fake_rtp_dtls_transport2_.get(), fake_rtcp_dtls_transport2_.get(),
flags2);
});
// Add stream information (SSRC) to the local content but not to the remote // content. This means that we per default know the SSRC of what we send but // not what we receive.
AddLegacyStreamInContent(kSsrc1, flags1, &local_media_content1_);
AddLegacyStreamInContent(kSsrc2, flags2, &local_media_content2_);
// If SSRC_MUX is used we also need to know the SSRC of the incoming stream. if (flags1 & SSRC_MUX) {
AddLegacyStreamInContent(kSsrc1, flags1, &remote_media_content1_);
} if (flags2 & SSRC_MUX) {
AddLegacyStreamInContent(kSsrc2, flags2, &remote_media_content2_);
}
}
std::unique_ptr<typename T::Channel> CreateChannel(
rtc::Thread* worker_thread,
rtc::Thread* network_thread,
std::unique_ptr<typename T::MediaSendChannel> ch_send,
std::unique_ptr<typename T::MediaReceiveChannel> ch_receive,
webrtc::RtpTransportInternal* rtp_transport, int flags);
void ConnectFakeTransports() {
SendTask(network_thread_, [this] { bool asymmetric = false; // Depending on test flags, could be using DTLS or raw packet transport. if (fake_rtp_dtls_transport1_ && fake_rtp_dtls_transport2_) {
fake_rtp_dtls_transport1_->SetDestination(
fake_rtp_dtls_transport2_.get(), asymmetric);
} if (fake_rtcp_dtls_transport1_ && fake_rtcp_dtls_transport2_) {
fake_rtcp_dtls_transport1_->SetDestination(
fake_rtcp_dtls_transport2_.get(), asymmetric);
} if (fake_rtp_packet_transport1_ && fake_rtp_packet_transport2_) {
fake_rtp_packet_transport1_->SetDestination(
fake_rtp_packet_transport2_.get(), asymmetric);
} if (fake_rtcp_packet_transport1_ && fake_rtcp_packet_transport2_) {
fake_rtcp_packet_transport1_->SetDestination(
fake_rtcp_packet_transport2_.get(), asymmetric);
}
}); // The transport becoming writable will asynchronously update the send state // on the worker thread; since this test uses the main thread as the worker // thread, we must process the message queue for this to occur.
WaitForThreads();
}
bool SendInitiate() {
std::string err; bool result = channel1_->SetLocalContent(&local_media_content1_,
SdpType::kOffer, err); if (result) {
channel1_->Enable(true);
FlushCurrentThread();
result = channel2_->SetRemoteContent(&remote_media_content1_,
SdpType::kOffer, err); if (result) {
ConnectFakeTransports();
result = channel2_->SetLocalContent(&local_media_content2_,
SdpType::kAnswer, err);
}
} return result;
}
// Creates a MediaContent with one stream. // kPcmuCodec is used as audio codec and kH264Codec is used as video codec. typename T::Content* CreateMediaContentWithStream(uint32_t ssrc) { typename T::Content* content = newtypename T::Content();
CreateContent(0, kPcmuCodec, kH264Codec, content);
AddLegacyStreamInContent(ssrc, 0, content); return content;
}
// Will manage the lifetime of a CallThread, making sure it's // destroyed before this object goes out of scope. class ScopedCallThread { public: explicit ScopedCallThread(absl::AnyInvocable<void() &&> functor)
: thread_(rtc::Thread::Create()) {
thread_->Start();
thread_->PostTask(std::move(functor));
}
void AddLegacyStreamInContent(uint32_t ssrc, int flags, typename T::Content* content) { // Base implementation.
}
// Utility method that calls BaseChannel::srtp_active() on the network thread // and returns the result. The `srtp_active()` state is maintained on the // network thread, which callers need to factor in. bool IsSrtpActive(std::unique_ptr<typename T::Channel>& channel) {
RTC_DCHECK(channel.get()); bool result;
SendTask(network_thread_, [&] { result = channel->srtp_active(); }); return result;
}
// Returns true iff the transport is set for a channel and rtcp_mux_enabled() // returns true. bool IsRtcpMuxEnabled(std::unique_ptr<typename T::Channel>& channel) {
RTC_DCHECK(channel.get()); bool result;
SendTask(network_thread_, [&] {
result = channel->rtp_transport() &&
channel->rtp_transport()->rtcp_mux_enabled();
}); return result;
}
// Tests that can be used by derived classes.
// Basic sanity check. void TestInit() {
CreateChannels(0, 0);
EXPECT_FALSE(IsSrtpActive(channel1_));
EXPECT_FALSE(media_send_channel1_impl()->sending()); if (verify_playout_) {
EXPECT_FALSE(media_receive_channel1_impl()->playout());
}
EXPECT_TRUE(media_send_channel1_impl()->send_codecs().empty());
EXPECT_TRUE(media_receive_channel1_impl()->recv_streams().empty());
EXPECT_TRUE(media_send_channel1_impl()->rtp_packets().empty()); // Basic sanity test for send and receive channel objects
EXPECT_EQ(channel1_->media_send_channel()->media_type(),
media_send_channel1_impl()->media_type());
EXPECT_EQ(channel1_->media_receive_channel()->media_type(),
media_receive_channel1_impl()->media_type());
EXPECT_EQ(channel1_->media_send_channel()->media_type(),
channel1_->media_receive_channel()->media_type());
}
// Test that SetLocalContent and SetRemoteContent properly configure // the codecs. void TestSetContents() {
CreateChannels(0, 0); typename T::Content content;
CreateContent(0, kPcmuCodec, kH264Codec, &content);
std::string err;
EXPECT_TRUE(channel1_->SetLocalContent(&content, SdpType::kOffer, err));
EXPECT_EQ(0U, media_send_channel1_impl()->send_codecs().size());
EXPECT_TRUE(channel1_->SetRemoteContent(&content, SdpType::kAnswer, err));
ASSERT_EQ(1U, media_send_channel1_impl()->send_codecs().size());
EXPECT_EQ(content.codecs()[0],
media_send_channel1_impl()->send_codecs()[0]);
}
// Test that SetLocalContent and SetRemoteContent properly configure // extmap-allow-mixed. void TestSetContentsExtmapAllowMixedCaller(bool offer, bool answer) { // For a caller, SetLocalContent() is called first with an offer and next // SetRemoteContent() is called with the answer.
CreateChannels(0, 0); typename T::Content content;
CreateContent(0, kPcmuCodec, kH264Codec, &content); auto offer_enum = offer ? (T::Content::kSession) : (T::Content::kNo); auto answer_enum = answer ? (T::Content::kSession) : (T::Content::kNo);
content.set_extmap_allow_mixed_enum(offer_enum);
std::string err;
EXPECT_TRUE(channel1_->SetLocalContent(&content, SdpType::kOffer, err));
content.set_extmap_allow_mixed_enum(answer_enum);
EXPECT_TRUE(channel1_->SetRemoteContent(&content, SdpType::kAnswer, err));
EXPECT_EQ(answer, media_send_channel1_impl()->ExtmapAllowMixed());
} void TestSetContentsExtmapAllowMixedCallee(bool offer, bool answer) { // For a callee, SetRemoteContent() is called first with an offer and next // SetLocalContent() is called with the answer.
CreateChannels(0, 0); typename T::Content content;
CreateContent(0, kPcmuCodec, kH264Codec, &content); auto offer_enum = offer ? (T::Content::kSession) : (T::Content::kNo); auto answer_enum = answer ? (T::Content::kSession) : (T::Content::kNo);
content.set_extmap_allow_mixed_enum(offer_enum);
std::string err;
EXPECT_TRUE(channel1_->SetRemoteContent(&content, SdpType::kOffer, err));
content.set_extmap_allow_mixed_enum(answer_enum);
EXPECT_TRUE(channel1_->SetLocalContent(&content, SdpType::kAnswer, err));
EXPECT_EQ(answer, media_send_channel1()->ExtmapAllowMixed());
}
// Test that SetLocalContent and SetRemoteContent properly deals // with an empty offer. void TestSetContentsNullOffer() {
CreateChannels(0, 0); typename T::Content content;
std::string err;
EXPECT_TRUE(channel1_->SetLocalContent(&content, SdpType::kOffer, err));
CreateContent(0, kPcmuCodec, kH264Codec, &content);
EXPECT_EQ(0U, media_send_channel1_impl()->send_codecs().size());
EXPECT_TRUE(channel1_->SetRemoteContent(&content, SdpType::kAnswer, err));
ASSERT_EQ(1U, media_send_channel1_impl()->send_codecs().size());
EXPECT_EQ(content.codecs()[0],
media_send_channel1_impl()->send_codecs()[0]);
}
// Test that SetLocalContent and SetRemoteContent properly set RTCP // mux. void TestSetContentsRtcpMux() {
CreateChannels(0, 0); typename T::Content content;
CreateContent(0, kPcmuCodec, kH264Codec, &content); // Both sides agree on mux. Should no longer be a separate RTCP channel.
content.set_rtcp_mux(true);
std::string err;
EXPECT_TRUE(channel1_->SetLocalContent(&content, SdpType::kOffer, err));
EXPECT_TRUE(channel1_->SetRemoteContent(&content, SdpType::kAnswer, err)); // Only initiator supports mux. Should still have a separate RTCP channel.
EXPECT_TRUE(channel2_->SetLocalContent(&content, SdpType::kOffer, err));
content.set_rtcp_mux(false);
EXPECT_TRUE(channel2_->SetRemoteContent(&content, SdpType::kAnswer, err));
}
// Test that SetLocalContent and SetRemoteContent properly // handles adding and removing StreamParams when the action is a full // SdpType::kOffer / SdpType::kAnswer. void TestChangeStreamParamsInContent() {
cricket::StreamParams stream1;
stream1.id = "stream1";
stream1.ssrcs.push_back(kSsrc1);
stream1.cname = "stream1_cname";
// Test that we only start playout and sending at the right times. void TestPlayoutAndSendingStates() {
CreateChannels(0, 0); if (verify_playout_) {
EXPECT_FALSE(media_receive_channel1_impl()->playout());
}
EXPECT_FALSE(media_send_channel1_impl()->sending()); if (verify_playout_) {
EXPECT_FALSE(media_receive_channel2_impl()->playout());
}
EXPECT_FALSE(media_send_channel2_impl()->sending());
channel1_->Enable(true);
FlushCurrentThread(); if (verify_playout_) {
EXPECT_FALSE(media_receive_channel1_impl()->playout());
}
EXPECT_FALSE(media_send_channel1_impl()->sending());
std::string err;
EXPECT_TRUE(channel1_->SetLocalContent(&local_media_content1_,
SdpType::kOffer, err)); if (verify_playout_) {
EXPECT_TRUE(media_receive_channel1_impl()->playout());
}
EXPECT_FALSE(media_send_channel1_impl()->sending());
EXPECT_TRUE(channel2_->SetRemoteContent(&local_media_content1_,
SdpType::kOffer, err)); if (verify_playout_) {
EXPECT_FALSE(media_receive_channel2_impl()->playout());
}
EXPECT_FALSE(media_send_channel2_impl()->sending());
EXPECT_TRUE(channel2_->SetLocalContent(&local_media_content2_,
SdpType::kAnswer, err)); if (verify_playout_) {
EXPECT_FALSE(media_receive_channel2_impl()->playout());
}
EXPECT_FALSE(media_send_channel2_impl()->sending());
ConnectFakeTransports(); if (verify_playout_) {
EXPECT_TRUE(media_receive_channel1_impl()->playout());
}
EXPECT_FALSE(media_send_channel1_impl()->sending()); if (verify_playout_) {
EXPECT_FALSE(media_receive_channel2_impl()->playout());
}
EXPECT_FALSE(media_send_channel2_impl()->sending());
channel2_->Enable(true);
FlushCurrentThread(); if (verify_playout_) {
EXPECT_TRUE(media_receive_channel2_impl()->playout());
}
EXPECT_TRUE(media_send_channel2_impl()->sending());
EXPECT_TRUE(channel1_->SetRemoteContent(&local_media_content2_,
SdpType::kAnswer, err)); if (verify_playout_) {
EXPECT_TRUE(media_receive_channel1_impl()->playout());
}
EXPECT_TRUE(media_send_channel1_impl()->sending());
}
// Test that changing the MediaContentDirection in the local and remote // session description start playout and sending at the right time. void TestMediaContentDirection() {
CreateChannels(0, 0); typename T::Content content1;
CreateContent(0, kPcmuCodec, kH264Codec, &content1); typename T::Content content2;
CreateContent(0, kPcmuCodec, kH264Codec, &content2); // Set `content2` to be InActive.
content2.set_direction(RtpTransceiverDirection::kInactive);
channel1_->Enable(true);
channel2_->Enable(true);
FlushCurrentThread(); if (verify_playout_) {
EXPECT_FALSE(media_receive_channel1_impl()->playout());
}
EXPECT_FALSE(media_send_channel1_impl()->sending()); if (verify_playout_) {
EXPECT_FALSE(media_receive_channel2_impl()->playout());
}
EXPECT_FALSE(media_send_channel2_impl()->sending());
if (verify_playout_) {
EXPECT_TRUE(media_receive_channel1_impl()->playout());
}
EXPECT_FALSE(media_send_channel1_impl()->sending()); // remote InActive if (verify_playout_) {
EXPECT_FALSE(media_receive_channel2_impl()->playout()); // local InActive
}
EXPECT_FALSE(media_send_channel2_impl()->sending()); // local InActive
// Update `content2` to be RecvOnly.
content2.set_direction(RtpTransceiverDirection::kRecvOnly);
EXPECT_TRUE(channel2_->SetLocalContent(&content2, SdpType::kPrAnswer, err));
EXPECT_TRUE(
channel1_->SetRemoteContent(&content2, SdpType::kPrAnswer, err));
if (verify_playout_) {
EXPECT_TRUE(media_receive_channel1_impl()->playout());
}
EXPECT_TRUE(media_send_channel1_impl()->sending()); if (verify_playout_) {
EXPECT_TRUE(media_receive_channel2_impl()->playout()); // local RecvOnly
}
EXPECT_FALSE(media_send_channel2_impl()->sending()); // local RecvOnly
// Update `content2` to be SendRecv.
content2.set_direction(RtpTransceiverDirection::kSendRecv);
EXPECT_TRUE(channel2_->SetLocalContent(&content2, SdpType::kAnswer, err));
EXPECT_TRUE(channel1_->SetRemoteContent(&content2, SdpType::kAnswer, err));
if (verify_playout_) {
EXPECT_TRUE(media_receive_channel1_impl()->playout());
}
EXPECT_TRUE(media_send_channel1_impl()->sending()); if (verify_playout_) {
EXPECT_TRUE(media_receive_channel2_impl()->playout());
}
EXPECT_TRUE(media_send_channel2_impl()->sending());
// Update `content2` to be inactive on the receiver while sending at the // sender.
content2.set_direction(RtpTransceiverDirection::kInactive);
EXPECT_TRUE(channel1_->SetLocalContent(&content1, SdpType::kOffer, err));
EXPECT_TRUE(channel2_->SetRemoteContent(&content1, SdpType::kOffer, err));
EXPECT_TRUE(channel2_->SetLocalContent(&content2, SdpType::kAnswer, err));
content2.set_direction(RtpTransceiverDirection::kRecvOnly);
EXPECT_TRUE(channel1_->SetRemoteContent(&content2, SdpType::kAnswer, err)); if (verify_playout_) {
EXPECT_FALSE(media_receive_channel2_impl()->playout());
}
EXPECT_TRUE(media_send_channel1_impl()->sending());
// Tests that when the transport channel signals a candidate pair change // event, the media channel will receive a call on the network route change. void TestNetworkRouteChanges() { static constexpr uint16_t kLocalNetId = 1; static constexpr uint16_t kRemoteNetId = 2; static constexpr int kLastPacketId = 100; // Ipv4(20) + UDP(8). static constexpr int kTransportOverheadPerPacket = 28; static constexpr int kSrtpOverheadPerPacket = 10;
// Need to wait for the threads before calling // `set_num_network_route_changes` because the network route would be set // when creating the channel.
WaitForThreads();
media_send_channel1_impl->set_num_network_route_changes(0);
SendTask(network_thread_, [this] {
rtc::NetworkRoute network_route; // The transport channel becomes disconnected.
fake_rtp_dtls_transport1_->ice_transport()->SignalNetworkRouteChanged(
std::optional<rtc::NetworkRoute>(network_route));
});
WaitForThreads();
EXPECT_EQ(1, media_send_channel1_impl->num_network_route_changes());
EXPECT_FALSE(media_send_channel1_impl->last_network_route().connected);
media_send_channel1_impl->set_num_network_route_changes(0);
// Test setting up a call. void TestCallSetup() {
CreateChannels(0, 0);
EXPECT_FALSE(IsSrtpActive(channel1_));
EXPECT_TRUE(SendInitiate()); if (verify_playout_) {
EXPECT_TRUE(media_receive_channel1_impl()->playout());
}
EXPECT_FALSE(media_send_channel1_impl()->sending());
EXPECT_TRUE(SendAccept());
EXPECT_FALSE(IsSrtpActive(channel1_));
EXPECT_TRUE(media_send_channel1_impl()->sending());
EXPECT_EQ(1U, media_send_channel1_impl()->send_codecs().size()); if (verify_playout_) {
EXPECT_TRUE(media_receive_channel2_impl()->playout());
}
EXPECT_TRUE(media_send_channel2_impl()->sending());
EXPECT_EQ(1U, media_send_channel2_impl()->send_codecs().size());
}
// Send voice RTP data to the other side and ensure it gets there. void SendRtpToRtp() {
CreateChannels(RTCP_MUX, RTCP_MUX);
EXPECT_TRUE(SendInitiate());
EXPECT_TRUE(SendAccept());
EXPECT_TRUE(IsRtcpMuxEnabled(channel1_));
EXPECT_TRUE(IsRtcpMuxEnabled(channel2_));
SendRtp1();
SendRtp2();
WaitForThreads();
EXPECT_TRUE(CheckRtp1());
EXPECT_TRUE(CheckRtp2());
EXPECT_TRUE(CheckNoRtp1());
EXPECT_TRUE(CheckNoRtp2());
}
// Test that we can send and receive early media when a provisional answer is // sent and received. The test uses SRTP, RTCP mux and SSRC mux. void SendEarlyMediaUsingRtcpMuxSrtp() { int sequence_number1_1 = 0, sequence_number2_2 = 0;
CreateChannels(SSRC_MUX | RTCP_MUX | DTLS, SSRC_MUX | RTCP_MUX | DTLS);
EXPECT_TRUE(SendOffer());
EXPECT_TRUE(SendProvisionalAnswer());
EXPECT_TRUE(IsSrtpActive(channel1_));
EXPECT_TRUE(IsSrtpActive(channel2_));
EXPECT_TRUE(IsRtcpMuxEnabled(channel1_));
EXPECT_TRUE(IsRtcpMuxEnabled(channel2_));
WaitForThreads(); // Wait for 'sending' flag go through network thread.
SendCustomRtp1(kSsrc1, ++sequence_number1_1);
WaitForThreads();
EXPECT_TRUE(CheckCustomRtp2(kSsrc1, sequence_number1_1));
// Send packets from callee and verify that it is received.
SendCustomRtp2(kSsrc2, ++sequence_number2_2);
WaitForThreads();
EXPECT_TRUE(CheckCustomRtp1(kSsrc2, sequence_number2_2));
// Complete call setup and ensure everything is still OK.
EXPECT_TRUE(SendFinalAnswer());
EXPECT_TRUE(IsSrtpActive(channel1_));
EXPECT_TRUE(IsSrtpActive(channel2_));
SendCustomRtp1(kSsrc1, ++sequence_number1_1);
SendCustomRtp2(kSsrc2, ++sequence_number2_2);
WaitForThreads();
EXPECT_TRUE(CheckCustomRtp2(kSsrc1, sequence_number1_1));
EXPECT_TRUE(CheckCustomRtp1(kSsrc2, sequence_number2_2));
}
// Test that we properly send RTP without SRTP from a thread. void SendRtpToRtpOnThread() {
CreateChannels(0, 0);
EXPECT_TRUE(SendInitiate());
EXPECT_TRUE(SendAccept());
ScopedCallThread send_rtp1([this] { SendRtp1(); });
ScopedCallThread send_rtp2([this] { SendRtp2(); });
rtc::Thread* involved_threads[] = {send_rtp1.thread(), send_rtp2.thread()};
WaitForThreads(involved_threads);
EXPECT_TRUE(CheckRtp1());
EXPECT_TRUE(CheckRtp2());
EXPECT_TRUE(CheckNoRtp1());
EXPECT_TRUE(CheckNoRtp2());
}
// Test that the mediachannel retains its sending state after the transport // becomes non-writable. void SendWithWritabilityLoss() {
CreateChannels(RTCP_MUX, RTCP_MUX);
EXPECT_TRUE(SendInitiate());
EXPECT_TRUE(SendAccept());
EXPECT_TRUE(IsRtcpMuxEnabled(channel1_));
EXPECT_TRUE(IsRtcpMuxEnabled(channel2_));
SendRtp1();
SendRtp2();
WaitForThreads();
EXPECT_TRUE(CheckRtp1());
EXPECT_TRUE(CheckRtp2());
EXPECT_TRUE(CheckNoRtp1());
EXPECT_TRUE(CheckNoRtp2());
// Lose writability, which should fail.
SendTask(network_thread_,
[this] { fake_rtp_dtls_transport1_->SetWritable(false); });
SendRtp1();
SendRtp2();
WaitForThreads();
EXPECT_TRUE(CheckRtp1());
EXPECT_TRUE(CheckNoRtp2());
void SendBundleToBundle(constint* pl_types, int len, bool rtcp_mux, bool secure) {
ASSERT_EQ(2, len); int sequence_number1_1 = 0, sequence_number2_2 = 0; // Only pl_type1 was added to the bundle filter for both `channel1_` // and `channel2_`. int pl_type1 = pl_types[0]; int pl_type2 = pl_types[1]; int flags = SSRC_MUX; if (secure)
flags |= DTLS; if (rtcp_mux) {
flags |= RTCP_MUX;
}
CreateChannels(flags, flags);
EXPECT_TRUE(SendInitiate());
EXPECT_TRUE(SendAccept());
// Test that when a channel gets new RtpTransport with a call to // `SetRtpTransport`, the socket options from the old RtpTransport is merged // with the options on the new one.
// For example, audio and video may use separate socket options, but initially // be unbundled, then later become bundled. When this happens, their preferred // socket options should be merged to the underlying transport they share. void SocketOptionsMergedOnSetTransport() {
constexpr int kSndBufSize = 4000;
constexpr int kRcvBufSize = 8000;
void CreateSimulcastContent(const std::vector<std::string>& rids, typename T::Content* content) {
std::vector<RidDescription> rid_descriptions; for (const std::string& name : rids) {
rid_descriptions.push_back(RidDescription(name, RidDirection::kSend));
}
StreamParams stream;
stream.set_rids(rid_descriptions);
CreateContent(0, kPcmuCodec, kH264Codec, content); // This is for unified plan, so there can be only one StreamParams.
content->mutable_streams().clear();
content->AddStream(stream);
}
// Create a similar offer. SetLocalContent should not remove and add.
CreateSimulcastContent({"f", "h", "q"}, &content2);
EXPECT_TRUE(channel1_->SetLocalContent(&content2, SdpType::kOffer, err));
VerifySimulcastStreamParams(content2.streams()[0], channel1_.get());
StreamParams stream2 = channel1_->local_streams()[0]; // Check that the streams are identical (SSRCs didn't change).
EXPECT_EQ(stream1, stream2);
// Create third offer that has same RIDs in different order.
CreateSimulcastContent({"f", "q", "h"}, &content3);
EXPECT_TRUE(channel1_->SetLocalContent(&content3, SdpType::kOffer, err));
VerifySimulcastStreamParams(content3.streams()[0], channel1_.get());
}
protected: void WaitForThreads() { WaitForThreads(rtc::ArrayView<rtc::Thread*>()); } staticvoid ProcessThreadQueue(rtc::Thread* thread) {
RTC_DCHECK(thread->IsCurrent()); while (!thread->empty()) {
thread->ProcessMessages(0);
}
} staticvoid FlushCurrentThread() {
rtc::Thread::Current()->ProcessMessages(0);
} void WaitForThreads(rtc::ArrayView<rtc::Thread*> threads) { // `threads` and current thread post packets to network thread. for (rtc::Thread* thread : threads) {
SendTask(thread, [thread] { ProcessThreadQueue(thread); });
}
ProcessThreadQueue(rtc::Thread::Current()); // Network thread move them around and post back to worker = current thread. if (!network_thread_->IsCurrent()) {
SendTask(network_thread_,
[this] { ProcessThreadQueue(network_thread_); });
} // Worker thread = current Thread process received messages.
ProcessThreadQueue(rtc::Thread::Current());
}
// Accessors that return the FakeMedia<type>SendChannel object. // Note that these depend on getting the object back that was // passed to the channel constructor. // T::MediaSendChannel is either FakeVoiceMediaSendChannel or // FakeVideoMediaSendChannel. typename T::MediaSendChannel* media_send_channel1_impl() {
RTC_DCHECK(channel1_); returnstatic_cast<typename T::MediaSendChannel*>(
channel1_->media_send_channel());
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.