Do not create a temporary transport channel when using max-bundle

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}
This commit is contained in:
skvlad
2016-05-17 17:49:52 -07:00
committed by Commit bot
parent 3a0a0f4b0d
commit 6c87a67b63
9 changed files with 204 additions and 91 deletions

View File

@ -1739,13 +1739,41 @@ void WebRtcSession::RemoveUnusedChannels(const SessionDescription* desc) {
}
}
// TODO(mallinath) - Add a correct error code if the channels are not created
// due to BUNDLE is enabled but rtcp-mux is disabled.
// Returns the name of the transport channel when BUNDLE is enabled, or nullptr
// if the channel is not part of any bundle.
const std::string* WebRtcSession::GetBundleTransportName(
const cricket::ContentInfo* content,
const cricket::ContentGroup* bundle) {
if (!bundle) {
return nullptr;
}
const std::string* first_content_name = bundle->FirstContentName();
if (!first_content_name) {
LOG(LS_WARNING) << "Tried to BUNDLE with no contents.";
return nullptr;
}
if (!bundle->HasContentName(content->name)) {
LOG(LS_WARNING) << content->name << " is not part of any bundle group";
return nullptr;
}
LOG(LS_INFO) << "Bundling " << content->name << " on " << *first_content_name;
return first_content_name;
}
bool WebRtcSession::CreateChannels(const SessionDescription* desc) {
const cricket::ContentGroup* bundle_group = nullptr;
if (bundle_policy_ == PeerConnectionInterface::kBundlePolicyMaxBundle) {
bundle_group = desc->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
if (!bundle_group) {
LOG(LS_WARNING) << "max-bundle specified without BUNDLE specified";
return false;
}
}
// Creating the media channels and transport proxies.
const cricket::ContentInfo* voice = cricket::GetFirstAudioContent(desc);
if (voice && !voice->rejected && !voice_channel_) {
if (!CreateVoiceChannel(voice)) {
if (!CreateVoiceChannel(voice,
GetBundleTransportName(voice, bundle_group))) {
LOG(LS_ERROR) << "Failed to create voice channel.";
return false;
}
@ -1753,7 +1781,8 @@ bool WebRtcSession::CreateChannels(const SessionDescription* desc) {
const cricket::ContentInfo* video = cricket::GetFirstVideoContent(desc);
if (video && !video->rejected && !video_channel_) {
if (!CreateVideoChannel(video)) {
if (!CreateVideoChannel(video,
GetBundleTransportName(video, bundle_group))) {
LOG(LS_ERROR) << "Failed to create video channel.";
return false;
}
@ -1762,48 +1791,29 @@ bool WebRtcSession::CreateChannels(const SessionDescription* desc) {
const cricket::ContentInfo* data = cricket::GetFirstDataContent(desc);
if (data_channel_type_ != cricket::DCT_NONE &&
data && !data->rejected && !data_channel_) {
if (!CreateDataChannel(data)) {
if (!CreateDataChannel(data, GetBundleTransportName(data, bundle_group))) {
LOG(LS_ERROR) << "Failed to create data channel.";
return false;
}
}
if (rtcp_mux_policy_ == PeerConnectionInterface::kRtcpMuxPolicyRequire) {
if (voice_channel()) {
voice_channel()->ActivateRtcpMux();
}
if (video_channel()) {
video_channel()->ActivateRtcpMux();
}
if (data_channel()) {
data_channel()->ActivateRtcpMux();
}
}
// Enable BUNDLE immediately when kBundlePolicyMaxBundle is in effect.
if (bundle_policy_ == PeerConnectionInterface::kBundlePolicyMaxBundle) {
const cricket::ContentGroup* bundle_group = desc->GetGroupByName(
cricket::GROUP_TYPE_BUNDLE);
if (!bundle_group) {
LOG(LS_WARNING) << "max-bundle specified without BUNDLE specified";
return false;
}
if (!EnableBundle(*bundle_group)) {
LOG(LS_WARNING) << "max-bundle failed to enable bundling.";
return false;
}
}
return true;
}
bool WebRtcSession::CreateVoiceChannel(const cricket::ContentInfo* content) {
bool WebRtcSession::CreateVoiceChannel(const cricket::ContentInfo* content,
const std::string* bundle_transport) {
bool require_rtcp_mux =
rtcp_mux_policy_ == PeerConnectionInterface::kRtcpMuxPolicyRequire;
bool create_rtcp_transport_channel = !require_rtcp_mux;
voice_channel_.reset(channel_manager_->CreateVoiceChannel(
media_controller_, transport_controller_.get(), content->name, true,
audio_options_));
media_controller_, transport_controller_.get(), content->name,
bundle_transport, create_rtcp_transport_channel, audio_options_));
if (!voice_channel_) {
return false;
}
if (require_rtcp_mux) {
voice_channel_->ActivateRtcpMux();
}
voice_channel_->SignalDtlsSetupFailure.connect(
this, &WebRtcSession::OnDtlsSetupFailure);
@ -1814,14 +1824,20 @@ bool WebRtcSession::CreateVoiceChannel(const cricket::ContentInfo* content) {
return true;
}
bool WebRtcSession::CreateVideoChannel(const cricket::ContentInfo* content) {
bool WebRtcSession::CreateVideoChannel(const cricket::ContentInfo* content,
const std::string* bundle_transport) {
bool require_rtcp_mux =
rtcp_mux_policy_ == PeerConnectionInterface::kRtcpMuxPolicyRequire;
bool create_rtcp_transport_channel = !require_rtcp_mux;
video_channel_.reset(channel_manager_->CreateVideoChannel(
media_controller_, transport_controller_.get(), content->name, true,
video_options_));
media_controller_, transport_controller_.get(), content->name,
bundle_transport, create_rtcp_transport_channel, video_options_));
if (!video_channel_) {
return false;
}
if (require_rtcp_mux) {
video_channel_->ActivateRtcpMux();
}
video_channel_->SignalDtlsSetupFailure.connect(
this, &WebRtcSession::OnDtlsSetupFailure);
@ -1831,13 +1847,21 @@ bool WebRtcSession::CreateVideoChannel(const cricket::ContentInfo* content) {
return true;
}
bool WebRtcSession::CreateDataChannel(const cricket::ContentInfo* content) {
bool WebRtcSession::CreateDataChannel(const cricket::ContentInfo* content,
const std::string* bundle_transport) {
bool sctp = (data_channel_type_ == cricket::DCT_SCTP);
bool require_rtcp_mux =
rtcp_mux_policy_ == PeerConnectionInterface::kRtcpMuxPolicyRequire;
bool create_rtcp_transport_channel = !sctp && !require_rtcp_mux;
data_channel_.reset(channel_manager_->CreateDataChannel(
transport_controller_.get(), content->name, !sctp, data_channel_type_));
transport_controller_.get(), content->name, bundle_transport,
create_rtcp_transport_channel, data_channel_type_));
if (!data_channel_) {
return false;
}
if (require_rtcp_mux) {
data_channel_->ActivateRtcpMux();
}
if (sctp) {
data_channel_->SignalDataReceived.connect(
@ -1950,6 +1974,9 @@ bool WebRtcSession::ValidateSessionDescription(
return BadSdp(source, type, kBundleWithoutRtcpMux, err_desc);
}
// TODO(skvlad): When the local rtcp-mux policy is Require, reject any
// m-lines that do not rtcp-mux enabled.
// Verify m-lines in Answer when compared against Offer.
if (action == kAnswer) {
const cricket::SessionDescription* offer_desc =

View File

@ -386,6 +386,12 @@ class WebRtcSession : public AudioProviderInterface,
const std::string& content_name,
cricket::TransportDescription* info);
// Returns the name of the transport channel when BUNDLE is enabled, or
// nullptr if the channel is not part of any bundle.
const std::string* GetBundleTransportName(
const cricket::ContentInfo* content,
const cricket::ContentGroup* bundle);
// Cause all the BaseChannels in the bundle group to have the same
// transport channel.
bool EnableBundle(const cricket::ContentGroup& bundle);
@ -412,9 +418,12 @@ class WebRtcSession : public AudioProviderInterface,
bool CreateChannels(const cricket::SessionDescription* desc);
// Helper methods to create media channels.
bool CreateVoiceChannel(const cricket::ContentInfo* content);
bool CreateVideoChannel(const cricket::ContentInfo* content);
bool CreateDataChannel(const cricket::ContentInfo* content);
bool CreateVoiceChannel(const cricket::ContentInfo* content,
const std::string* bundle_transport);
bool CreateVideoChannel(const cricket::ContentInfo* content,
const std::string* bundle_transport);
bool CreateDataChannel(const cricket::ContentInfo* content,
const std::string* bundle_transport);
// Listens to SCTP CONTROL messages on unused SIDs and process them as OPEN
// messages.

View File

@ -163,6 +163,7 @@ class MockIceObserver : public webrtc::IceObserver {
void OnIceConnectionChange(
PeerConnectionInterface::IceConnectionState new_state) override {
ice_connection_state_ = new_state;
ice_connection_state_history_.push_back(new_state);
}
void OnIceGatheringChange(
PeerConnectionInterface::IceGatheringState new_state) override {
@ -202,6 +203,8 @@ class MockIceObserver : public webrtc::IceObserver {
std::vector<cricket::Candidate> mline_1_candidates_;
PeerConnectionInterface::IceConnectionState ice_connection_state_;
PeerConnectionInterface::IceGatheringState ice_gathering_state_;
std::vector<PeerConnectionInterface::IceConnectionState>
ice_connection_state_history_;
size_t num_candidates_removed_ = 0;
};
@ -3263,6 +3266,60 @@ TEST_F(WebRtcSessionTest, TestMaxBundleWithSetRemoteDescriptionFirst) {
session_->video_rtp_transport_channel());
}
// Adding a new channel to a BUNDLE which is already connected should directly
// assign the bundle transport to the channel, without first setting a
// disconnected non-bundle transport and then replacing it. The application
// should not receive any changes in the ICE state.
TEST_F(WebRtcSessionTest, TestAddChannelToConnectedBundle) {
LoopbackNetworkConfiguration config;
LoopbackNetworkManager loopback_network_manager(this, config);
// Both BUNDLE and RTCP-mux need to be enabled for the ICE state to remain
// connected. Disabling either of these two means that we need to wait for the
// answer to find out if more transports are needed.
configuration_.bundle_policy =
PeerConnectionInterface::kBundlePolicyMaxBundle;
configuration_.rtcp_mux_policy =
PeerConnectionInterface::kRtcpMuxPolicyRequire;
options_.disable_encryption = true;
Init();
// Negotiate an audio channel with MAX_BUNDLE enabled.
SendAudioOnlyStream2();
SessionDescriptionInterface* offer = CreateOffer();
SetLocalDescriptionWithoutError(offer);
EXPECT_EQ_WAIT(PeerConnectionInterface::kIceGatheringComplete,
observer_.ice_gathering_state_, kIceCandidatesTimeout);
std::string sdp;
offer->ToString(&sdp);
SessionDescriptionInterface* answer = webrtc::CreateSessionDescription(
JsepSessionDescription::kAnswer, sdp, nullptr);
ASSERT_TRUE(answer != NULL);
SetRemoteDescriptionWithoutError(answer);
// Wait for the ICE state to stabilize.
EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionCompleted,
observer_.ice_connection_state_, kIceCandidatesTimeout);
observer_.ice_connection_state_history_.clear();
// Now add a video channel which should be using the same bundle transport.
SendAudioVideoStream2();
offer = CreateOffer();
offer->ToString(&sdp);
SetLocalDescriptionWithoutError(offer);
answer = webrtc::CreateSessionDescription(JsepSessionDescription::kAnswer,
sdp, nullptr);
ASSERT_TRUE(answer != NULL);
SetRemoteDescriptionWithoutError(answer);
// Wait for ICE state to stabilize
rtc::Thread::Current()->ProcessMessages(0);
EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionCompleted,
observer_.ice_connection_state_, kIceCandidatesTimeout);
// No ICE state changes are expected to happen.
EXPECT_EQ(0, observer_.ice_connection_state_history_.size());
}
TEST_F(WebRtcSessionTest, TestRequireRtcpMux) {
InitWithRtcpMuxPolicy(PeerConnectionInterface::kRtcpMuxPolicyRequire);
SendAudioVideoStream1();

View File

@ -229,8 +229,9 @@ void BaseChannel::DestroyTransportChannels_n() {
network_thread_->Clear(this);
}
bool BaseChannel::Init_w() {
if (!network_thread_->Invoke<bool>(Bind(&BaseChannel::InitNetwork_n, this))) {
bool BaseChannel::Init_w(const std::string* bundle_transport_name) {
if (!network_thread_->Invoke<bool>(
Bind(&BaseChannel::InitNetwork_n, this, bundle_transport_name))) {
return false;
}
@ -241,9 +242,11 @@ bool BaseChannel::Init_w() {
return true;
}
bool BaseChannel::InitNetwork_n() {
bool BaseChannel::InitNetwork_n(const std::string* bundle_transport_name) {
RTC_DCHECK(network_thread_->IsCurrent());
if (!SetTransport_n(content_name())) {
const std::string& transport_name =
(bundle_transport_name ? *bundle_transport_name : content_name());
if (!SetTransport_n(transport_name)) {
return false;
}
@ -1476,8 +1479,8 @@ VoiceChannel::~VoiceChannel() {
Deinit();
}
bool VoiceChannel::Init_w() {
if (!BaseChannel::Init_w()) {
bool VoiceChannel::Init_w(const std::string* bundle_transport_name) {
if (!BaseChannel::Init_w(bundle_transport_name)) {
return false;
}
return true;
@ -1831,8 +1834,8 @@ VideoChannel::VideoChannel(rtc::Thread* worker_thread,
content_name,
rtcp) {}
bool VideoChannel::Init_w() {
if (!BaseChannel::Init_w()) {
bool VideoChannel::Init_w(const std::string* bundle_transport_name) {
if (!BaseChannel::Init_w(bundle_transport_name)) {
return false;
}
return true;
@ -2103,8 +2106,8 @@ DataChannel::~DataChannel() {
Deinit();
}
bool DataChannel::Init_w() {
if (!BaseChannel::Init_w()) {
bool DataChannel::Init_w(const std::string* bundle_transport_name) {
if (!BaseChannel::Init_w(bundle_transport_name)) {
return false;
}
media_channel()->SignalDataReceived.connect(

View File

@ -77,7 +77,7 @@ class BaseChannel
const std::string& content_name,
bool rtcp);
virtual ~BaseChannel();
bool Init_w();
bool Init_w(const std::string* bundle_transport_name);
// Deinit may be called multiple times and is simply ignored if it's already
// done.
void Deinit();
@ -313,7 +313,7 @@ class BaseChannel
}
private:
bool InitNetwork_n();
bool InitNetwork_n(const std::string* bundle_transport_name);
void DisconnectTransportChannels_n();
void DestroyTransportChannels_n();
void SignalSentPacket_n(TransportChannel* channel,
@ -373,7 +373,7 @@ class VoiceChannel : public BaseChannel {
const std::string& content_name,
bool rtcp);
~VoiceChannel();
bool Init_w();
bool Init_w(const std::string* bundle_transport_name);
// Configure sending media on the stream with SSRC |ssrc|
// If there is only one sending stream SSRC 0 can be used.
@ -489,7 +489,7 @@ class VideoChannel : public BaseChannel {
const std::string& content_name,
bool rtcp);
~VideoChannel();
bool Init_w();
bool Init_w(const std::string* bundle_transport_name);
// downcasts a MediaChannel
VideoMediaChannel* media_channel() const override {
@ -564,7 +564,7 @@ class DataChannel : public BaseChannel {
const std::string& content_name,
bool rtcp);
~DataChannel();
bool Init_w();
bool Init_w(const std::string* bundle_transport_name);
virtual bool SendData(const SendDataParams& params,
const rtc::CopyOnWriteBuffer& payload,

View File

@ -190,7 +190,7 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> {
typename T::Channel* channel =
new typename T::Channel(worker_thread, network_thread, engine, ch,
transport_controller, cricket::CN_AUDIO, rtcp);
if (!channel->Init_w()) {
if (!channel->Init_w(nullptr)) {
delete channel;
channel = NULL;
}
@ -2018,7 +2018,7 @@ cricket::VideoChannel* ChannelTest<VideoTraits>::CreateChannel(
cricket::VideoChannel* channel =
new cricket::VideoChannel(worker_thread, network_thread, ch,
transport_controller, cricket::CN_VIDEO, rtcp);
if (!channel->Init_w()) {
if (!channel->Init_w(nullptr)) {
delete channel;
channel = NULL;
}
@ -3258,7 +3258,7 @@ cricket::DataChannel* ChannelTest<DataTraits>::CreateChannel(
cricket::DataChannel* channel =
new cricket::DataChannel(worker_thread, network_thread, ch,
transport_controller, cricket::CN_DATA, rtcp);
if (!channel->Init_w()) {
if (!channel->Init_w(nullptr)) {
delete channel;
channel = NULL;
}

View File

@ -205,17 +205,20 @@ VoiceChannel* ChannelManager::CreateVoiceChannel(
webrtc::MediaControllerInterface* media_controller,
TransportController* transport_controller,
const std::string& content_name,
const std::string* bundle_transport_name,
bool rtcp,
const AudioOptions& options) {
return worker_thread_->Invoke<VoiceChannel*>(
Bind(&ChannelManager::CreateVoiceChannel_w, this, media_controller,
transport_controller, content_name, rtcp, options));
transport_controller, content_name, bundle_transport_name, rtcp,
options));
}
VoiceChannel* ChannelManager::CreateVoiceChannel_w(
webrtc::MediaControllerInterface* media_controller,
TransportController* transport_controller,
const std::string& content_name,
const std::string* bundle_transport_name,
bool rtcp,
const AudioOptions& options) {
ASSERT(initialized_);
@ -229,7 +232,7 @@ VoiceChannel* ChannelManager::CreateVoiceChannel_w(
VoiceChannel* voice_channel =
new VoiceChannel(worker_thread_, network_thread_, media_engine_.get(),
media_channel, transport_controller, content_name, rtcp);
if (!voice_channel->Init_w()) {
if (!voice_channel->Init_w(bundle_transport_name)) {
delete voice_channel;
return nullptr;
}
@ -263,17 +266,20 @@ VideoChannel* ChannelManager::CreateVideoChannel(
webrtc::MediaControllerInterface* media_controller,
TransportController* transport_controller,
const std::string& content_name,
const std::string* bundle_transport_name,
bool rtcp,
const VideoOptions& options) {
return worker_thread_->Invoke<VideoChannel*>(
Bind(&ChannelManager::CreateVideoChannel_w, this, media_controller,
transport_controller, content_name, rtcp, options));
transport_controller, content_name, bundle_transport_name, rtcp,
options));
}
VideoChannel* ChannelManager::CreateVideoChannel_w(
webrtc::MediaControllerInterface* media_controller,
TransportController* transport_controller,
const std::string& content_name,
const std::string* bundle_transport_name,
bool rtcp,
const VideoOptions& options) {
ASSERT(initialized_);
@ -288,7 +294,7 @@ VideoChannel* ChannelManager::CreateVideoChannel_w(
VideoChannel* video_channel =
new VideoChannel(worker_thread_, network_thread_, media_channel,
transport_controller, content_name, rtcp);
if (!video_channel->Init_w()) {
if (!video_channel->Init_w(bundle_transport_name)) {
delete video_channel;
return NULL;
}
@ -322,16 +328,18 @@ void ChannelManager::DestroyVideoChannel_w(VideoChannel* video_channel) {
DataChannel* ChannelManager::CreateDataChannel(
TransportController* transport_controller,
const std::string& content_name,
const std::string* bundle_transport_name,
bool rtcp,
DataChannelType channel_type) {
return worker_thread_->Invoke<DataChannel*>(
Bind(&ChannelManager::CreateDataChannel_w, this, transport_controller,
content_name, rtcp, channel_type));
content_name, bundle_transport_name, rtcp, channel_type));
}
DataChannel* ChannelManager::CreateDataChannel_w(
TransportController* transport_controller,
const std::string& content_name,
const std::string* bundle_transport_name,
bool rtcp,
DataChannelType data_channel_type) {
// This is ok to alloc from a thread other than the worker thread.
@ -347,7 +355,7 @@ DataChannel* ChannelManager::CreateDataChannel_w(
DataChannel* data_channel =
new DataChannel(worker_thread_, network_thread_, media_channel,
transport_controller, content_name, rtcp);
if (!data_channel->Init_w()) {
if (!data_channel->Init_w(bundle_transport_name)) {
LOG(LS_WARNING) << "Failed to init data channel.";
delete data_channel;
return NULL;

View File

@ -91,6 +91,7 @@ class ChannelManager {
webrtc::MediaControllerInterface* media_controller,
TransportController* transport_controller,
const std::string& content_name,
const std::string* bundle_transport_name,
bool rtcp,
const AudioOptions& options);
// Destroys a voice channel created with the Create API.
@ -101,12 +102,14 @@ class ChannelManager {
webrtc::MediaControllerInterface* media_controller,
TransportController* transport_controller,
const std::string& content_name,
const std::string* bundle_transport_name,
bool rtcp,
const VideoOptions& options);
// Destroys a video channel created with the Create API.
void DestroyVideoChannel(VideoChannel* video_channel);
DataChannel* CreateDataChannel(TransportController* transport_controller,
const std::string& content_name,
const std::string* bundle_transport_name,
bool rtcp,
DataChannelType data_channel_type);
// Destroys a data channel created with the Create API.
@ -158,6 +161,7 @@ class ChannelManager {
webrtc::MediaControllerInterface* media_controller,
TransportController* transport_controller,
const std::string& content_name,
const std::string* bundle_transport_name,
bool rtcp,
const AudioOptions& options);
void DestroyVoiceChannel_w(VoiceChannel* voice_channel);
@ -165,11 +169,13 @@ class ChannelManager {
webrtc::MediaControllerInterface* media_controller,
TransportController* transport_controller,
const std::string& content_name,
const std::string* bundle_transport_name,
bool rtcp,
const VideoOptions& options);
void DestroyVideoChannel_w(VideoChannel* video_channel);
DataChannel* CreateDataChannel_w(TransportController* transport_controller,
const std::string& content_name,
const std::string* bundle_transport_name,
bool rtcp,
DataChannelType data_channel_type);
void DestroyDataChannel_w(DataChannel* data_channel);

View File

@ -98,16 +98,17 @@ TEST_F(ChannelManagerTest, StartupShutdownOnThread) {
// 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, false, AudioOptions());
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, false, VideoOptions());
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, false, cricket::DCT_RTP);
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);
@ -125,16 +126,17 @@ TEST_F(ChannelManagerTest, CreateDestroyChannelsOnThread) {
delete transport_controller_;
transport_controller_ =
new cricket::FakeTransportController(&network_, ICEROLE_CONTROLLING);
cricket::VoiceChannel* voice_channel =
cm_->CreateVoiceChannel(&fake_mc_, transport_controller_,
cricket::CN_AUDIO, false, AudioOptions());
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, false, VideoOptions());
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, false, cricket::DCT_RTP);
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);
@ -152,16 +154,17 @@ TEST_F(ChannelManagerTest, NoTransportChannelTest) {
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, false, AudioOptions());
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, false, VideoOptions());
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, false, cricket::DCT_RTP);
cricket::DataChannel* data_channel =
cm_->CreateDataChannel(transport_controller_, cricket::CN_DATA, nullptr,
false, cricket::DCT_RTP);
EXPECT_TRUE(data_channel == nullptr);
cm_->Terminate();
}