/* * 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 "webrtc/pc/channelmanager.h" #include #include "webrtc/media/base/device.h" #include "webrtc/media/base/rtpdataengine.h" #include "webrtc/pc/srtpfilter.h" #include "webrtc/rtc_base/bind.h" #include "webrtc/rtc_base/checks.h" #include "webrtc/rtc_base/logging.h" #include "webrtc/rtc_base/stringencode.h" #include "webrtc/rtc_base/stringutils.h" #include "webrtc/rtc_base/trace_event.h" namespace cricket { using rtc::Bind; ChannelManager::ChannelManager(std::unique_ptr me, std::unique_ptr dme, rtc::Thread* thread) { Construct(std::move(me), std::move(dme), thread, thread); } ChannelManager::ChannelManager(std::unique_ptr me, rtc::Thread* worker_thread, rtc::Thread* network_thread) { Construct(std::move(me), std::unique_ptr(new RtpDataEngine()), worker_thread, network_thread); } void ChannelManager::Construct(std::unique_ptr me, std::unique_ptr dme, rtc::Thread* worker_thread, rtc::Thread* network_thread) { media_engine_ = std::move(me); data_media_engine_ = std::move(dme); initialized_ = false; main_thread_ = rtc::Thread::Current(); worker_thread_ = worker_thread; network_thread_ = network_thread; capturing_ = false; enable_rtx_ = false; } ChannelManager::~ChannelManager() { if (initialized_) { Terminate(); // If srtp is initialized (done by the Channel) then we must call // srtp_shutdown to free all crypto kernel lists. But we need to make sure // shutdown always called at the end, after channels are destroyed. // ChannelManager d'tor is always called last, it's safe place to call // shutdown. ShutdownSrtp(); } // The media engine needs to be deleted on the worker thread for thread safe // destruction, worker_thread_->Invoke( RTC_FROM_HERE, Bind(&ChannelManager::DestructorDeletes_w, this)); } bool ChannelManager::SetVideoRtxEnabled(bool enable) { // To be safe, this call is only allowed before initialization. Apps like // Flute only have a singleton ChannelManager and we don't want this flag to // be toggled between calls or when there's concurrent calls. We expect apps // to enable this at startup and retain that setting for the lifetime of the // app. if (!initialized_) { enable_rtx_ = enable; return true; } else { LOG(LS_WARNING) << "Cannot toggle rtx after initialization!"; return false; } } void ChannelManager::GetSupportedAudioSendCodecs( std::vector* codecs) const { if (!media_engine_) { return; } *codecs = media_engine_->audio_send_codecs(); } void ChannelManager::GetSupportedAudioReceiveCodecs( std::vector* codecs) const { if (!media_engine_) { return; } *codecs = media_engine_->audio_recv_codecs(); } void ChannelManager::GetSupportedAudioRtpHeaderExtensions( RtpHeaderExtensions* ext) const { if (!media_engine_) { return; } *ext = media_engine_->GetAudioCapabilities().header_extensions; } void ChannelManager::GetSupportedVideoCodecs( std::vector* codecs) const { if (!media_engine_) { return; } codecs->clear(); std::vector video_codecs = media_engine_->video_codecs(); for (const auto& video_codec : video_codecs) { if (!enable_rtx_ && _stricmp(kRtxCodecName, video_codec.name.c_str()) == 0) { continue; } codecs->push_back(video_codec); } } void ChannelManager::GetSupportedVideoRtpHeaderExtensions( RtpHeaderExtensions* ext) const { if (!media_engine_) { return; } *ext = media_engine_->GetVideoCapabilities().header_extensions; } void ChannelManager::GetSupportedDataCodecs( std::vector* codecs) const { if (!data_media_engine_) { return; } *codecs = data_media_engine_->data_codecs(); } bool ChannelManager::Init() { RTC_DCHECK(!initialized_); if (initialized_) { return false; } RTC_DCHECK(network_thread_); RTC_DCHECK(worker_thread_); if (!network_thread_->IsCurrent()) { // Do not allow invoking calls to other threads on the network thread. network_thread_->Invoke( RTC_FROM_HERE, rtc::Bind(&rtc::Thread::SetAllowBlockingCalls, network_thread_, false)); } initialized_ = worker_thread_->Invoke( RTC_FROM_HERE, Bind(&ChannelManager::InitMediaEngine_w, this)); RTC_DCHECK(initialized_); return initialized_; } bool ChannelManager::InitMediaEngine_w() { RTC_DCHECK(worker_thread_ == rtc::Thread::Current()); if (media_engine_) { return media_engine_->Init(); } return true; } void ChannelManager::Terminate() { RTC_DCHECK(initialized_); if (!initialized_) { return; } worker_thread_->Invoke(RTC_FROM_HERE, Bind(&ChannelManager::Terminate_w, this)); initialized_ = false; } void ChannelManager::DestructorDeletes_w() { RTC_DCHECK(worker_thread_ == rtc::Thread::Current()); media_engine_.reset(NULL); } void ChannelManager::Terminate_w() { RTC_DCHECK(worker_thread_ == rtc::Thread::Current()); // Need to destroy the voice/video channels while (!video_channels_.empty()) { DestroyVideoChannel_w(video_channels_.back()); } while (!voice_channels_.empty()) { DestroyVoiceChannel_w(voice_channels_.back()); } } 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( RTC_FROM_HERE, Bind(&ChannelManager::CreateVoiceChannel_w, this, 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( RTC_FROM_HERE, Bind(&ChannelManager::CreateVoiceChannel_w, this, call, media_config, nullptr, nullptr, rtp_transport, rtcp_transport, signaling_thread, content_name, srtp_required, options)); } 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(initialized_); RTC_DCHECK(worker_thread_ == rtc::Thread::Current()); RTC_DCHECK(nullptr != call); if (!media_engine_) { return nullptr; } VoiceMediaChannel* media_channel = media_engine_->CreateChannel( call, media_config, options); if (!media_channel) return nullptr; VoiceChannel* voice_channel = new VoiceChannel(worker_thread_, network_thread_, signaling_thread, media_engine_.get(), media_channel, content_name, rtcp_packet_transport == nullptr, srtp_required); if (!voice_channel->Init_w(rtp_dtls_transport, rtcp_dtls_transport, rtp_packet_transport, rtcp_packet_transport)) { delete voice_channel; return nullptr; } voice_channels_.push_back(voice_channel); return voice_channel; } void ChannelManager::DestroyVoiceChannel(VoiceChannel* voice_channel) { TRACE_EVENT0("webrtc", "ChannelManager::DestroyVoiceChannel"); if (voice_channel) { worker_thread_->Invoke( RTC_FROM_HERE, Bind(&ChannelManager::DestroyVoiceChannel_w, this, voice_channel)); } } void ChannelManager::DestroyVoiceChannel_w(VoiceChannel* voice_channel) { TRACE_EVENT0("webrtc", "ChannelManager::DestroyVoiceChannel_w"); // Destroy voice channel. RTC_DCHECK(initialized_); RTC_DCHECK(worker_thread_ == rtc::Thread::Current()); VoiceChannels::iterator it = std::find(voice_channels_.begin(), voice_channels_.end(), voice_channel); RTC_DCHECK(it != voice_channels_.end()); if (it == voice_channels_.end()) return; voice_channels_.erase(it); delete voice_channel; } 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( RTC_FROM_HERE, Bind(&ChannelManager::CreateVideoChannel_w, this, 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( RTC_FROM_HERE, Bind(&ChannelManager::CreateVideoChannel_w, this, call, media_config, nullptr, nullptr, rtp_transport, rtcp_transport, signaling_thread, content_name, srtp_required, options)); } 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(initialized_); RTC_DCHECK(worker_thread_ == rtc::Thread::Current()); RTC_DCHECK(nullptr != call); VideoMediaChannel* media_channel = media_engine_->CreateVideoChannel( call, media_config, options); if (media_channel == NULL) { return NULL; } VideoChannel* video_channel = new VideoChannel( worker_thread_, network_thread_, signaling_thread, media_channel, content_name, rtcp_packet_transport == nullptr, srtp_required); if (!video_channel->Init_w(rtp_dtls_transport, rtcp_dtls_transport, rtp_packet_transport, rtcp_packet_transport)) { delete video_channel; return NULL; } video_channels_.push_back(video_channel); return video_channel; } void ChannelManager::DestroyVideoChannel(VideoChannel* video_channel) { TRACE_EVENT0("webrtc", "ChannelManager::DestroyVideoChannel"); if (video_channel) { worker_thread_->Invoke( RTC_FROM_HERE, Bind(&ChannelManager::DestroyVideoChannel_w, this, video_channel)); } } void ChannelManager::DestroyVideoChannel_w(VideoChannel* video_channel) { TRACE_EVENT0("webrtc", "ChannelManager::DestroyVideoChannel_w"); // Destroy video channel. RTC_DCHECK(initialized_); RTC_DCHECK(worker_thread_ == rtc::Thread::Current()); VideoChannels::iterator it = std::find(video_channels_.begin(), video_channels_.end(), video_channel); RTC_DCHECK(it != video_channels_.end()); if (it == video_channels_.end()) return; video_channels_.erase(it); delete video_channel; } 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) { return worker_thread_->Invoke( RTC_FROM_HERE, Bind(&ChannelManager::CreateRtpDataChannel_w, this, media_config, rtp_transport, rtcp_transport, signaling_thread, content_name, srtp_required)); } RtpDataChannel* ChannelManager::CreateRtpDataChannel_w( const cricket::MediaConfig& media_config, DtlsTransportInternal* rtp_transport, DtlsTransportInternal* rtcp_transport, rtc::Thread* signaling_thread, const std::string& content_name, bool srtp_required) { // This is ok to alloc from a thread other than the worker thread. RTC_DCHECK(initialized_); DataMediaChannel* media_channel = data_media_engine_->CreateChannel(media_config); if (!media_channel) { LOG(LS_WARNING) << "Failed to create RTP data channel."; return nullptr; } RtpDataChannel* data_channel = new RtpDataChannel( worker_thread_, network_thread_, signaling_thread, media_channel, content_name, rtcp_transport == nullptr, srtp_required); if (!data_channel->Init_w(rtp_transport, rtcp_transport, rtp_transport, rtcp_transport)) { LOG(LS_WARNING) << "Failed to init data channel."; delete data_channel; return nullptr; } data_channels_.push_back(data_channel); return data_channel; } void ChannelManager::DestroyRtpDataChannel(RtpDataChannel* data_channel) { TRACE_EVENT0("webrtc", "ChannelManager::DestroyRtpDataChannel"); if (data_channel) { worker_thread_->Invoke( RTC_FROM_HERE, Bind(&ChannelManager::DestroyRtpDataChannel_w, this, data_channel)); } } void ChannelManager::DestroyRtpDataChannel_w(RtpDataChannel* data_channel) { TRACE_EVENT0("webrtc", "ChannelManager::DestroyRtpDataChannel_w"); // Destroy data channel. RTC_DCHECK(initialized_); RtpDataChannels::iterator it = std::find(data_channels_.begin(), data_channels_.end(), data_channel); RTC_DCHECK(it != data_channels_.end()); if (it == data_channels_.end()) return; data_channels_.erase(it); delete data_channel; } bool ChannelManager::StartAecDump(rtc::PlatformFile file, int64_t max_size_bytes) { return worker_thread_->Invoke( RTC_FROM_HERE, Bind(&MediaEngineInterface::StartAecDump, media_engine_.get(), file, max_size_bytes)); } void ChannelManager::StopAecDump() { worker_thread_->Invoke( RTC_FROM_HERE, Bind(&MediaEngineInterface::StopAecDump, media_engine_.get())); } } // namespace cricket