using ::testing::AllOf; using ::testing::ElementsAre; using ::testing::Field; using ::testing::HasSubstr; using ::webrtc::CreateTestFieldTrials; using ::webrtc::DtlsTransportInternal; using ::webrtc::FakeVoiceMediaReceiveChannel; using ::webrtc::FakeVoiceMediaSendChannel; using ::webrtc::FieldTrials; using ::webrtc::RidDescription; using ::webrtc::RidDirection; using ::webrtc::RTCError; using ::webrtc::RtpExtension; using ::webrtc::RtpTransceiverDirection; using ::webrtc::SdpType; using ::webrtc::StreamParams;
template <class ChannelT, class MediaSendChannelT, class MediaReceiveChannelT, class MediaSendChannelInterfaceT, class MediaReceiveChannelInterfaceT, class ContentT, class MediaInfoT, class OptionsT> class Traits { public: using Channel = ChannelT; using MediaSendChannel = MediaSendChannelT; using MediaReceiveChannel = MediaReceiveChannelT; using MediaSendChannelInterface = MediaSendChannelInterfaceT; using MediaReceiveChannelInterface = MediaReceiveChannelInterfaceT; using Content = ContentT; using MediaInfo = MediaInfoT; using Options = OptionsT;
};
class VoiceTraits : public Traits<webrtc::BaseChannel,
webrtc::FakeVoiceMediaSendChannel,
webrtc::FakeVoiceMediaReceiveChannel,
webrtc::VoiceMediaSendChannelInterface,
webrtc::VoiceMediaReceiveChannelInterface,
webrtc::AudioContentDescription,
webrtc::VoiceMediaInfo,
webrtc::AudioOptions> {};
class VideoTraits : public Traits<webrtc::BaseChannel,
webrtc::FakeVideoMediaSendChannel,
webrtc::FakeVideoMediaReceiveChannel,
webrtc::VideoMediaSendChannelInterface,
webrtc::VideoMediaReceiveChannelInterface,
webrtc::VideoContentDescription,
webrtc::VideoMediaInfo,
webrtc::VideoOptions> {};
// Base class for Voice/Video tests template <class T> class ChannelTest : public ::testing::Test { public: enum Flags {
RTCP_MUX = 0x1,
SSRC_MUX = 0x8,
DTLS = 0x10, // Use BaseChannel with PacketTransportInternal rather than // DtlsTransportInternal.
RAW_PACKET_TRANSPORT = 0x20,
};
void CreateChannels(std::unique_ptr<typename T::MediaSendChannel> ch1s,
std::unique_ptr<typename T::MediaReceiveChannel> ch1r,
std::unique_ptr<typename T::MediaSendChannel> ch2s,
std::unique_ptr<typename T::MediaReceiveChannel> ch2r, int flags1, int flags2) {
RTC_DCHECK(!channel1_);
RTC_DCHECK(!channel2_);
// 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);
webrtc::Thread* worker_thread = webrtc::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 webrtc::FakePacketTransport("channel1_rtp")); if (!(flags1 & RTCP_MUX)) {
fake_rtcp_packet_transport1_.reset( new webrtc::FakePacketTransport("channel1_rtcp"));
}
} else { // Confirmed to work with KT_RSA and KT_ECDSA.
fake_rtp_dtls_transport1_.reset(new webrtc::FakeDtlsTransport( "channel1", webrtc::ICE_CANDIDATE_COMPONENT_RTP, network_thread_)); if (!(flags1 & RTCP_MUX)) {
fake_rtcp_dtls_transport1_.reset(new webrtc::FakeDtlsTransport( "channel1", webrtc::ICE_CANDIDATE_COMPONENT_RTCP,
network_thread_));
} if (flags1 & DTLS) { auto cert1 = webrtc::RTCCertificate::Create(
webrtc::SSLIdentity::Create("session1", webrtc::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 webrtc::FakePacketTransport("channel2_rtp")); if (!(flags2 & RTCP_MUX)) {
fake_rtcp_packet_transport2_.reset( new webrtc::FakePacketTransport("channel2_rtcp"));
}
} else { // Confirmed to work with KT_RSA and KT_ECDSA.
fake_rtp_dtls_transport2_.reset(new webrtc::FakeDtlsTransport( "channel2", webrtc::ICE_CANDIDATE_COMPONENT_RTP, network_thread_)); if (!(flags2 & RTCP_MUX)) {
fake_rtcp_dtls_transport2_.reset(new webrtc::FakeDtlsTransport( "channel2", webrtc::ICE_CANDIDATE_COMPONENT_RTCP,
network_thread_));
} if (flags2 & DTLS) { auto cert2 = webrtc::RTCCertificate::Create(
webrtc::SSLIdentity::Create("session2", webrtc::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_);
}
}
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() {
RTCError result =
channel1_->SetLocalContent(&local_media_content1_, SdpType::kOffer); if (result.ok()) {
channel1_->Enable(true);
FlushCurrentThread();
result =
channel2_->SetRemoteContent(&remote_media_content1_, SdpType::kOffer); if (result.ok()) {
ConnectFakeTransports();
result = channel2_->SetLocalContent(&local_media_content2_,
SdpType::kAnswer);
}
} return result.ok();
}
// 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_(webrtc::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);
EXPECT_TRUE(channel1_->SetLocalContent(&content, SdpType::kOffer).ok());
EXPECT_EQ(0U, media_send_channel1_impl()->send_codecs().size());
EXPECT_TRUE(channel1_->SetRemoteContent(&content, SdpType::kAnswer).ok());
ASSERT_EQ(1U, media_send_channel1_impl()->send_codecs().size());
EXPECT_EQ(content.codecs()[0],
media_send_channel1_impl()->send_codecs()[0]);
}
typename T::Content local_updated;
CreateContent(/*flags=*/0, kPcmuCodec, kH264Codec, &local_updated);
local_updated.set_rtp_header_extensions({
RtpExtension(RtpExtension::kVideoRotationUri, 1),
});
RTCError error =
channel1_->SetLocalContent(&local_updated, SdpType::kOffer); // Expected to succeed because the mapping for ID 1 was cleared by the // previous SetLocalContent call (which set extensions to empty). // RtpTransport allows reuse when not in use by any active MID.
EXPECT_TRUE(error.ok());
}
// 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);
EXPECT_TRUE(channel1_->SetLocalContent(&content, SdpType::kOffer).ok());
content.set_extmap_allow_mixed_enum(answer_enum);
EXPECT_TRUE(channel1_->SetRemoteContent(&content, SdpType::kAnswer).ok());
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);
EXPECT_TRUE(channel1_->SetRemoteContent(&content, SdpType::kOffer).ok());
content.set_extmap_allow_mixed_enum(answer_enum);
EXPECT_TRUE(channel1_->SetLocalContent(&content, SdpType::kAnswer).ok());
EXPECT_EQ(answer, channel1_->media_send_channel()->ExtmapAllowMixed());
}
// Test that SetLocalContent and SetRemoteContent properly deals // with an empty offer. void TestSetContentsNullOffer() {
CreateChannels(0, 0); typename T::Content content;
EXPECT_TRUE(channel1_->SetLocalContent(&content, SdpType::kOffer).ok());
CreateContent(0, kPcmuCodec, kH264Codec, &content);
EXPECT_EQ(0U, media_send_channel1_impl()->send_codecs().size());
EXPECT_TRUE(channel1_->SetRemoteContent(&content, SdpType::kAnswer).ok());
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);
EXPECT_TRUE(channel1_->SetLocalContent(&content, SdpType::kOffer).ok());
EXPECT_TRUE(channel1_->SetRemoteContent(&content, SdpType::kAnswer).ok()); // Only initiator supports mux. Should still have a separate RTCP channel.
EXPECT_TRUE(channel2_->SetLocalContent(&content, SdpType::kOffer).ok());
content.set_rtcp_mux(false);
EXPECT_TRUE(channel2_->SetRemoteContent(&content, SdpType::kAnswer).ok());
}
// Test that SetLocalContent and SetRemoteContent properly set RTCP // reduced_size. void TestSetContentsRtcpReducedSize() {
CreateChannels(0, 0); typename T::Content content;
CreateContent(0, kPcmuCodec, kH264Codec, &content); // Both sides agree on reduced size.
content.set_rtcp_reduced_size(true); // The RTCP mode is a send property and should be configured based on // the remote content and not the local content.
EXPECT_TRUE(channel1_->SetLocalContent(&content, SdpType::kOffer).ok());
EXPECT_EQ(media_receive_channel1_impl()->RtcpMode(),
webrtc::RtcpMode::kCompound);
EXPECT_TRUE(channel1_->SetRemoteContent(&content, SdpType::kAnswer).ok());
EXPECT_EQ(media_receive_channel1_impl()->RtcpMode(),
webrtc::RtcpMode::kReducedSize); // Only initiator supports reduced size.
EXPECT_TRUE(channel2_->SetLocalContent(&content, SdpType::kOffer).ok());
EXPECT_EQ(media_receive_channel2_impl()->RtcpMode(),
webrtc::RtcpMode::kCompound);
content.set_rtcp_reduced_size(false);
EXPECT_TRUE(channel2_->SetRemoteContent(&content, SdpType::kAnswer).ok());
EXPECT_EQ(media_receive_channel2_impl()->RtcpMode(),
webrtc::RtcpMode::kCompound); // Peer renegotiates without reduced size.
EXPECT_TRUE(channel1_->SetRemoteContent(&content, SdpType::kAnswer).ok());
EXPECT_EQ(media_receive_channel1_impl()->RtcpMode(),
webrtc::RtcpMode::kCompound);
}
// Test that SetLocalContent and SetRemoteContent properly // handles adding and removing StreamParams when the action is a full // SdpType::kOffer / SdpType::kAnswer. void TestChangeStreamParamsInContent() {
webrtc::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());
EXPECT_TRUE(
channel1_->SetLocalContent(&local_media_content1_, SdpType::kOffer)
.ok()); 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)
.ok()); 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)
.ok()); 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)
.ok()); 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).ok());
EXPECT_TRUE(
channel1_->SetRemoteContent(&content2, SdpType::kPrAnswer).ok());
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).ok());
EXPECT_TRUE(channel1_->SetRemoteContent(&content2, SdpType::kAnswer).ok());
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).ok());
EXPECT_TRUE(channel2_->SetRemoteContent(&content1, SdpType::kOffer).ok());
EXPECT_TRUE(channel2_->SetLocalContent(&content2, SdpType::kAnswer).ok());
content2.set_direction(RtpTransceiverDirection::kRecvOnly);
EXPECT_TRUE(channel1_->SetRemoteContent(&content2, SdpType::kAnswer).ok()); if (verify_playout_) {
EXPECT_FALSE(media_receive_channel2_impl()->playout());
}
EXPECT_TRUE(media_send_channel1_impl()->sending());
// 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(); });
webrtc::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(std::span<constint, 2> pl_types, bool rtcp_mux, bool secure) { 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).ok());
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).ok());
VerifySimulcastStreamParams(content3.streams()[0], channel1_.get());
}
protected: void WaitForThreads() { WaitForThreads(std::span<webrtc::Thread*>()); } staticvoid ProcessThreadQueue(webrtc::Thread* thread) {
RTC_DCHECK(thread->IsCurrent()); while (!thread->empty()) {
thread->ProcessMessages(0);
}
} void FlushCurrentThread() { main_thread_.Flush(); } void WaitForThreads(std::span<webrtc::Thread*> threads) { // `threads` and current thread post packets to network thread. for (webrtc::Thread* thread : threads) {
SendTask(thread, [thread] { ProcessThreadQueue(thread); });
}
ProcessThreadQueue(webrtc::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(webrtc::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());
}
class VoiceChannelSingleThreadTest : public ChannelTest<VoiceTraits> { public: using Base = ChannelTest<VoiceTraits>;
VoiceChannelSingleThreadTest()
: Base(true, kPcmuFrame, kRtcpReport, NetworkIsWorker::Yes) {}
};
class VoiceChannelDoubleThreadTest : public ChannelTest<VoiceTraits> { public: using Base = ChannelTest<VoiceTraits>;
VoiceChannelDoubleThreadTest()
: Base(true, kPcmuFrame, kRtcpReport, NetworkIsWorker::No) {}
};
class VoiceChannelWithEncryptedRtpHeaderExtensionsSingleThreadTest
: public ChannelTest<VoiceTraits> { public: using Base = ChannelTest<VoiceTraits>;
VoiceChannelWithEncryptedRtpHeaderExtensionsSingleThreadTest()
: Base(true,
kPcmuFrameWithExtensions,
kRtcpReport,
NetworkIsWorker::Yes) {}
};
class VoiceChannelWithEncryptedRtpHeaderExtensionsDoubleThreadTest
: public ChannelTest<VoiceTraits> { public: using Base = ChannelTest<VoiceTraits>;
VoiceChannelWithEncryptedRtpHeaderExtensionsDoubleThreadTest()
: Base(true, kPcmuFrameWithExtensions, kRtcpReport, NetworkIsWorker::No) {
}
};
class VideoChannelSingleThreadTest : public ChannelTest<VideoTraits> { public: using Base = ChannelTest<VideoTraits>;
VideoChannelSingleThreadTest()
: Base(false, kH264Packet, kRtcpReport, NetworkIsWorker::Yes) {}
};
class VideoChannelDoubleThreadTest : public ChannelTest<VideoTraits> { public: using Base = ChannelTest<VideoTraits>;
VideoChannelDoubleThreadTest()
: Base(false, kH264Packet, kRtcpReport, NetworkIsWorker::No) {}
};
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.