With this change, when max-bundle and rtcp-mux are both enabled, we no longer create and destroy a temporary transport channel when a media channel gets added. Instead, the media channel uses the correct bundled transport channel from the start. This fixes a bug where adding a media type would cause the ICE state to briefly become Disconnected and then immediately recover. The temporary channel was created in a non-writable state, which caused the TransportController to declare the ICE state to be Disconnected (as not all transport channels were writable). Right after creation, the temporary channel was then destroyed and the ICE state went back to the correct one. BUG=webrtc:5856 Review-Url: https://codereview.webrtc.org/1972493002 Cr-Commit-Position: refs/heads/master@{#12781}
238 lines
8.5 KiB
C++
238 lines
8.5 KiB
C++
/*
|
|
* Copyright 2008 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 "webrtc/api/fakemediacontroller.h"
|
|
#include "webrtc/base/gunit.h"
|
|
#include "webrtc/base/logging.h"
|
|
#include "webrtc/base/thread.h"
|
|
#include "webrtc/media/base/fakemediaengine.h"
|
|
#include "webrtc/media/base/fakevideocapturer.h"
|
|
#include "webrtc/media/base/testutils.h"
|
|
#include "webrtc/media/engine/fakewebrtccall.h"
|
|
#include "webrtc/p2p/base/faketransportcontroller.h"
|
|
#include "webrtc/pc/channelmanager.h"
|
|
|
|
namespace cricket {
|
|
|
|
static const AudioCodec kAudioCodecs[] = {
|
|
AudioCodec(97, "voice", 1, 2, 3), AudioCodec(111, "OPUS", 48000, 32000, 2),
|
|
};
|
|
|
|
static const VideoCodec kVideoCodecs[] = {
|
|
VideoCodec(99, "H264", 100, 200, 300),
|
|
VideoCodec(100, "VP8", 100, 200, 300), VideoCodec(96, "rtx", 100, 200, 300),
|
|
};
|
|
|
|
class ChannelManagerTest : public testing::Test {
|
|
protected:
|
|
ChannelManagerTest()
|
|
: fme_(new cricket::FakeMediaEngine()),
|
|
fdme_(new cricket::FakeDataEngine()),
|
|
cm_(new cricket::ChannelManager(fme_,
|
|
fdme_,
|
|
rtc::Thread::Current())),
|
|
fake_call_(webrtc::Call::Config()),
|
|
fake_mc_(cm_, &fake_call_),
|
|
transport_controller_(
|
|
new cricket::FakeTransportController(ICEROLE_CONTROLLING)) {}
|
|
|
|
virtual void SetUp() {
|
|
fme_->SetAudioCodecs(MAKE_VECTOR(kAudioCodecs));
|
|
fme_->SetVideoCodecs(MAKE_VECTOR(kVideoCodecs));
|
|
}
|
|
|
|
virtual void TearDown() {
|
|
delete transport_controller_;
|
|
delete cm_;
|
|
cm_ = NULL;
|
|
fdme_ = NULL;
|
|
fme_ = NULL;
|
|
}
|
|
|
|
rtc::Thread network_;
|
|
rtc::Thread worker_;
|
|
cricket::FakeMediaEngine* fme_;
|
|
cricket::FakeDataEngine* fdme_;
|
|
cricket::ChannelManager* cm_;
|
|
cricket::FakeCall fake_call_;
|
|
cricket::FakeMediaController fake_mc_;
|
|
cricket::FakeTransportController* transport_controller_;
|
|
};
|
|
|
|
// Test that we startup/shutdown properly.
|
|
TEST_F(ChannelManagerTest, StartupShutdown) {
|
|
EXPECT_FALSE(cm_->initialized());
|
|
EXPECT_EQ(rtc::Thread::Current(), cm_->worker_thread());
|
|
EXPECT_TRUE(cm_->Init());
|
|
EXPECT_TRUE(cm_->initialized());
|
|
cm_->Terminate();
|
|
EXPECT_FALSE(cm_->initialized());
|
|
}
|
|
|
|
// Test that we startup/shutdown properly with a worker thread.
|
|
TEST_F(ChannelManagerTest, StartupShutdownOnThread) {
|
|
network_.Start();
|
|
worker_.Start();
|
|
EXPECT_FALSE(cm_->initialized());
|
|
EXPECT_EQ(rtc::Thread::Current(), cm_->worker_thread());
|
|
EXPECT_TRUE(cm_->set_network_thread(&network_));
|
|
EXPECT_EQ(&network_, cm_->network_thread());
|
|
EXPECT_TRUE(cm_->set_worker_thread(&worker_));
|
|
EXPECT_EQ(&worker_, cm_->worker_thread());
|
|
EXPECT_TRUE(cm_->Init());
|
|
EXPECT_TRUE(cm_->initialized());
|
|
// Setting the network or worker thread while initialized should fail.
|
|
EXPECT_FALSE(cm_->set_network_thread(rtc::Thread::Current()));
|
|
EXPECT_FALSE(cm_->set_worker_thread(rtc::Thread::Current()));
|
|
cm_->Terminate();
|
|
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::VoiceChannel* voice_channel = cm_->CreateVoiceChannel(
|
|
&fake_mc_, transport_controller_, cricket::CN_AUDIO, nullptr, false,
|
|
AudioOptions());
|
|
EXPECT_TRUE(voice_channel != nullptr);
|
|
cricket::VideoChannel* video_channel = cm_->CreateVideoChannel(
|
|
&fake_mc_, transport_controller_, cricket::CN_VIDEO, nullptr, false,
|
|
VideoOptions());
|
|
EXPECT_TRUE(video_channel != nullptr);
|
|
cricket::DataChannel* data_channel =
|
|
cm_->CreateDataChannel(transport_controller_, cricket::CN_DATA, nullptr,
|
|
false, cricket::DCT_RTP);
|
|
EXPECT_TRUE(data_channel != nullptr);
|
|
cm_->DestroyVideoChannel(video_channel);
|
|
cm_->DestroyVoiceChannel(voice_channel);
|
|
cm_->DestroyDataChannel(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_));
|
|
EXPECT_TRUE(cm_->set_network_thread(&network_));
|
|
EXPECT_TRUE(cm_->Init());
|
|
delete transport_controller_;
|
|
transport_controller_ =
|
|
new cricket::FakeTransportController(&network_, ICEROLE_CONTROLLING);
|
|
cricket::VoiceChannel* voice_channel = cm_->CreateVoiceChannel(
|
|
&fake_mc_, transport_controller_, cricket::CN_AUDIO, nullptr, false,
|
|
AudioOptions());
|
|
EXPECT_TRUE(voice_channel != nullptr);
|
|
cricket::VideoChannel* video_channel = cm_->CreateVideoChannel(
|
|
&fake_mc_, transport_controller_, cricket::CN_VIDEO, nullptr, false,
|
|
VideoOptions());
|
|
EXPECT_TRUE(video_channel != nullptr);
|
|
cricket::DataChannel* data_channel =
|
|
cm_->CreateDataChannel(transport_controller_, cricket::CN_DATA, nullptr,
|
|
false, cricket::DCT_RTP);
|
|
EXPECT_TRUE(data_channel != nullptr);
|
|
cm_->DestroyVideoChannel(video_channel);
|
|
cm_->DestroyVoiceChannel(voice_channel);
|
|
cm_->DestroyDataChannel(data_channel);
|
|
cm_->Terminate();
|
|
}
|
|
|
|
// Test that we fail to create a voice/video channel if the session is unable
|
|
// to create a cricket::TransportChannel
|
|
TEST_F(ChannelManagerTest, NoTransportChannelTest) {
|
|
EXPECT_TRUE(cm_->Init());
|
|
transport_controller_->set_fail_channel_creation(true);
|
|
// The test is useless unless the session does not fail creating
|
|
// cricket::TransportChannel.
|
|
ASSERT_TRUE(transport_controller_->CreateTransportChannel_n(
|
|
"audio", cricket::ICE_CANDIDATE_COMPONENT_RTP) == nullptr);
|
|
|
|
cricket::VoiceChannel* voice_channel = cm_->CreateVoiceChannel(
|
|
&fake_mc_, transport_controller_, cricket::CN_AUDIO, nullptr, false,
|
|
AudioOptions());
|
|
EXPECT_TRUE(voice_channel == nullptr);
|
|
cricket::VideoChannel* video_channel = cm_->CreateVideoChannel(
|
|
&fake_mc_, transport_controller_, cricket::CN_VIDEO, nullptr, false,
|
|
VideoOptions());
|
|
EXPECT_TRUE(video_channel == nullptr);
|
|
cricket::DataChannel* data_channel =
|
|
cm_->CreateDataChannel(transport_controller_, cricket::CN_DATA, nullptr,
|
|
false, cricket::DCT_RTP);
|
|
EXPECT_TRUE(data_channel == nullptr);
|
|
cm_->Terminate();
|
|
}
|
|
|
|
TEST_F(ChannelManagerTest, GetSetOutputVolumeBeforeInit) {
|
|
int level;
|
|
// Before init, SetOutputVolume() remembers the volume but does not change the
|
|
// volume of the engine. GetOutputVolume() should fail.
|
|
EXPECT_EQ(-1, fme_->output_volume());
|
|
EXPECT_FALSE(cm_->GetOutputVolume(&level));
|
|
EXPECT_FALSE(cm_->SetOutputVolume(-1)); // Invalid volume.
|
|
EXPECT_TRUE(cm_->SetOutputVolume(99));
|
|
EXPECT_EQ(-1, fme_->output_volume());
|
|
|
|
// Init() will apply the remembered volume.
|
|
EXPECT_TRUE(cm_->Init());
|
|
EXPECT_TRUE(cm_->GetOutputVolume(&level));
|
|
EXPECT_EQ(99, level);
|
|
EXPECT_EQ(level, fme_->output_volume());
|
|
|
|
EXPECT_TRUE(cm_->SetOutputVolume(60));
|
|
EXPECT_TRUE(cm_->GetOutputVolume(&level));
|
|
EXPECT_EQ(60, level);
|
|
EXPECT_EQ(level, fme_->output_volume());
|
|
}
|
|
|
|
TEST_F(ChannelManagerTest, GetSetOutputVolume) {
|
|
int level;
|
|
EXPECT_TRUE(cm_->Init());
|
|
EXPECT_TRUE(cm_->GetOutputVolume(&level));
|
|
EXPECT_EQ(level, fme_->output_volume());
|
|
|
|
EXPECT_FALSE(cm_->SetOutputVolume(-1)); // Invalid volume.
|
|
EXPECT_TRUE(cm_->SetOutputVolume(60));
|
|
EXPECT_EQ(60, fme_->output_volume());
|
|
EXPECT_TRUE(cm_->GetOutputVolume(&level));
|
|
EXPECT_EQ(60, level);
|
|
}
|
|
|
|
TEST_F(ChannelManagerTest, SetVideoRtxEnabled) {
|
|
std::vector<VideoCodec> codecs;
|
|
const VideoCodec rtx_codec(96, "rtx", 0, 0, 0);
|
|
|
|
// By default RTX is disabled.
|
|
cm_->GetSupportedVideoCodecs(&codecs);
|
|
EXPECT_FALSE(ContainsMatchingCodec(codecs, rtx_codec));
|
|
|
|
// Enable and check.
|
|
EXPECT_TRUE(cm_->SetVideoRtxEnabled(true));
|
|
cm_->GetSupportedVideoCodecs(&codecs);
|
|
EXPECT_TRUE(ContainsMatchingCodec(codecs, rtx_codec));
|
|
|
|
// Disable and check.
|
|
EXPECT_TRUE(cm_->SetVideoRtxEnabled(false));
|
|
cm_->GetSupportedVideoCodecs(&codecs);
|
|
EXPECT_FALSE(ContainsMatchingCodec(codecs, rtx_codec));
|
|
|
|
// Cannot toggle rtx after initialization.
|
|
EXPECT_TRUE(cm_->Init());
|
|
EXPECT_FALSE(cm_->SetVideoRtxEnabled(true));
|
|
EXPECT_FALSE(cm_->SetVideoRtxEnabled(false));
|
|
|
|
// Can set again after terminate.
|
|
cm_->Terminate();
|
|
EXPECT_TRUE(cm_->SetVideoRtxEnabled(true));
|
|
cm_->GetSupportedVideoCodecs(&codecs);
|
|
EXPECT_TRUE(ContainsMatchingCodec(codecs, rtx_codec));
|
|
}
|
|
|
|
} // namespace cricket
|