| /* |
| * Copyright (c) 2012 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 "video_engine/vie_channel_manager.h" |
| |
| #include "engine_configurations.h" |
| #include "modules/rtp_rtcp/interface/rtp_rtcp.h" |
| #include "modules/utility/interface/process_thread.h" |
| #include "system_wrappers/interface/critical_section_wrapper.h" |
| #include "system_wrappers/interface/map_wrapper.h" |
| #include "system_wrappers/interface/trace.h" |
| #include "video_engine/vie_channel.h" |
| #include "video_engine/vie_defines.h" |
| #include "video_engine/vie_encoder.h" |
| #include "video_engine/vie_remb.h" |
| #include "voice_engine/main/interface/voe_video_sync.h" |
| |
| namespace webrtc { |
| |
| ViEChannelManager::ViEChannelManager( |
| int engine_id, |
| int number_of_cores, |
| ViEPerformanceMonitor& vie_performance_monitor) |
| : channel_id_critsect_(CriticalSectionWrapper::CreateCriticalSection()), |
| engine_id_(engine_id), |
| number_of_cores_(number_of_cores), |
| vie_performance_monitor_(vie_performance_monitor), |
| free_channel_ids_(new bool[kViEMaxNumberOfChannels]), |
| free_channel_ids_size_(kViEMaxNumberOfChannels), |
| voice_sync_interface_(NULL), |
| remb_(new VieRemb(engine_id)), |
| voice_engine_(NULL), |
| module_process_thread_(NULL) { |
| WEBRTC_TRACE(kTraceMemory, kTraceVideo, ViEId(engine_id), |
| "ViEChannelManager::ViEChannelManager(engine_id: %d)", |
| engine_id); |
| for (int idx = 0; idx < free_channel_ids_size_; idx++) { |
| free_channel_ids_[idx] = true; |
| } |
| } |
| |
| ViEChannelManager::~ViEChannelManager() { |
| WEBRTC_TRACE(kTraceMemory, kTraceVideo, ViEId(engine_id_), |
| "ViEChannelManager Destructor, engine_id: %d", engine_id_); |
| |
| module_process_thread_->DeRegisterModule(remb_.get()); |
| while (channel_map_.size() > 0) { |
| ChannelMap::iterator it = channel_map_.begin(); |
| // DeleteChannel will erase this channel from the map and invalidate |it|. |
| DeleteChannel(it->first); |
| } |
| |
| if (voice_sync_interface_) { |
| voice_sync_interface_->Release(); |
| } |
| if (channel_id_critsect_) { |
| delete channel_id_critsect_; |
| channel_id_critsect_ = NULL; |
| } |
| if (free_channel_ids_) { |
| delete[] free_channel_ids_; |
| free_channel_ids_ = NULL; |
| free_channel_ids_size_ = 0; |
| } |
| } |
| |
| void ViEChannelManager::SetModuleProcessThread( |
| ProcessThread& module_process_thread) { |
| assert(!module_process_thread_); |
| module_process_thread_ = &module_process_thread; |
| module_process_thread_->RegisterModule(remb_.get()); |
| } |
| |
| int ViEChannelManager::CreateChannel(int& channel_id) { |
| CriticalSectionScoped cs(*channel_id_critsect_); |
| |
| // Get a free id for the new channel. |
| if (!GetFreeChannelId(channel_id)) { |
| WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_), |
| "Max number of channels reached: %d", channel_map_.size()); |
| return -1; |
| } |
| |
| ViEChannel* vie_channel = new ViEChannel(channel_id, engine_id_, |
| number_of_cores_, |
| *module_process_thread_); |
| if (!vie_channel) { |
| ReturnChannelId(channel_id); |
| return -1; |
| } |
| if (vie_channel->Init() != 0) { |
| WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_), |
| "%s could not init channel", __FUNCTION__, channel_id); |
| ReturnChannelId(channel_id); |
| delete vie_channel; |
| vie_channel = NULL; |
| return -1; |
| } |
| |
| // There is no ViEEncoder for this channel, create one with default settings. |
| ViEEncoder* vie_encoder = new ViEEncoder(engine_id_, channel_id, |
| number_of_cores_, |
| *module_process_thread_); |
| if (!vie_encoder) { |
| WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_), |
| "%s(video_channel_id: %d) - Could not create a new encoder", |
| __FUNCTION__, channel_id); |
| delete vie_channel; |
| return -1; |
| } |
| |
| vie_encoder_map_[channel_id] = vie_encoder; |
| channel_map_[channel_id] = vie_channel; |
| |
| // Register the channel at the encoder. |
| RtpRtcp* send_rtp_rtcp_module = vie_encoder->SendRtpRtcpModule(); |
| if (vie_channel->RegisterSendRtpRtcpModule(*send_rtp_rtcp_module) != 0) { |
| assert(false); |
| vie_encoder_map_.erase(channel_id); |
| channel_map_.erase(channel_id); |
| ReturnChannelId(channel_id); |
| delete vie_channel; |
| WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id), |
| "%s: Could not register rtp module %d", __FUNCTION__, |
| channel_id); |
| return -1; |
| } |
| return 0; |
| } |
| |
| int ViEChannelManager::CreateChannel(int& channel_id, int original_channel) { |
| CriticalSectionScoped cs(*channel_id_critsect_); |
| |
| // Check that original_channel already exists. |
| ViEEncoder* vie_encoder = ViEEncoderPtr(original_channel); |
| if (!vie_encoder) { |
| WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_), |
| "%s: Original channel doesn't exist", __FUNCTION__, |
| original_channel); |
| return -1; |
| } |
| VideoCodec video_codec; |
| vie_encoder->GetEncoder(video_codec); |
| |
| // Get a free id for the new channel. |
| if (GetFreeChannelId(channel_id) == false) { |
| WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_), |
| "Max number of channels reached: %d", channel_map_.size()); |
| return -1; |
| } |
| ViEChannel* vie_channel = new ViEChannel(channel_id, engine_id_, |
| number_of_cores_, |
| *module_process_thread_); |
| if (!vie_channel) { |
| ReturnChannelId(channel_id); |
| return -1; |
| } |
| if (vie_channel->Init() != 0) { |
| WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_), |
| "%s could not init channel", __FUNCTION__, channel_id); |
| ReturnChannelId(channel_id); |
| delete vie_channel; |
| vie_channel = NULL; |
| return -1; |
| } |
| vie_encoder_map_[channel_id] = vie_encoder; |
| |
| // Set the same encoder settings for the channel as used by the master |
| // channel. Do this before attaching rtp module to ensure all rtp children has |
| // the same codec type. |
| VideoCodec encoder; |
| if (vie_encoder->GetEncoder(encoder) == 0) { |
| vie_channel->SetSendCodec(encoder); |
| } |
| channel_map_[channel_id] = vie_channel; |
| |
| // Register the channel at the encoder. |
| RtpRtcp* send_rtp_rtcp_module = vie_encoder->SendRtpRtcpModule(); |
| if (vie_channel->RegisterSendRtpRtcpModule(*send_rtp_rtcp_module) != 0) { |
| assert(false); |
| vie_encoder_map_.erase(channel_id); |
| channel_map_.erase(channel_id); |
| ReturnChannelId(channel_id); |
| delete vie_channel; |
| WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id), |
| "%s: Could not register rtp module %d", __FUNCTION__, |
| channel_id); |
| return -1; |
| } |
| return 0; |
| } |
| |
| int ViEChannelManager::DeleteChannel(int channel_id) { |
| ViEChannel* vie_channel = NULL; |
| ViEEncoder* vie_encoder = NULL; |
| { |
| // Write lock to make sure no one is using the channel. |
| ViEManagerWriteScoped wl(*this); |
| |
| // Protect the map. |
| CriticalSectionScoped cs(*channel_id_critsect_); |
| ChannelMap::iterator c_it = channel_map_.find(channel_id); |
| if (c_it == channel_map_.end()) { |
| // No such channel. |
| WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_), |
| "%s Channel doesn't exist: %d", __FUNCTION__, channel_id); |
| return -1; |
| } |
| vie_channel = c_it->second; |
| channel_map_.erase(c_it); |
| |
| // Deregister possible remb modules. |
| RtpRtcp* rtp_module = vie_channel->rtp_rtcp(); |
| remb_->RemoveRembSender(rtp_module); |
| remb_->RemoveReceiveChannel(rtp_module); |
| |
| // Deregister the channel from the ViEEncoder to stop the media flow. |
| vie_channel->DeregisterSendRtpRtcpModule(); |
| ReturnChannelId(channel_id); |
| |
| // Find the encoder object. |
| EncoderMap::iterator e_it = vie_encoder_map_.find(channel_id); |
| assert(e_it != vie_encoder_map_.end()); |
| vie_encoder = e_it->second; |
| |
| remb_->RemoveSendChannel(vie_encoder->SendRtpRtcpModule()); |
| |
| // Check if other channels are using the same encoder. |
| if (ChannelUsingViEEncoder(channel_id)) { |
| // Don't delete the ViEEncoder, at least one other channel is using it. |
| WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_), |
| "%s ViEEncoder removed from map for channel %d, not deleted", |
| __FUNCTION__, channel_id); |
| vie_encoder = NULL; |
| } else { |
| WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_), |
| "%s ViEEncoder deleted for channel %d", __FUNCTION__, |
| channel_id); |
| // Delete later when we've released the critsect. |
| } |
| |
| // We can't erase the item before we've checked for other channels using |
| // same ViEEncoder. |
| vie_encoder_map_.erase(e_it); |
| } |
| |
| // Leave the write critsect before deleting the objects. |
| // Deleting a channel can cause other objects, such as renderers, to be |
| // deleted, which might take time. |
| if (vie_encoder) { |
| delete vie_encoder; |
| } |
| delete vie_channel; |
| |
| WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_), |
| "%s Channel %d deleted", __FUNCTION__, channel_id); |
| return 0; |
| } |
| |
| int ViEChannelManager::SetVoiceEngine(VoiceEngine* voice_engine) { |
| // Write lock to make sure no one is using the channel. |
| ViEManagerWriteScoped wl(*this); |
| |
| CriticalSectionScoped cs(*channel_id_critsect_); |
| |
| VoEVideoSync* sync_interface = NULL; |
| if (voice_engine) { |
| // Get new sync interface. |
| sync_interface = VoEVideoSync::GetInterface(voice_engine); |
| if (!sync_interface) { |
| WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_), |
| "%s Can't get audio sync interface from VoiceEngine.", |
| __FUNCTION__); |
| return -1; |
| } |
| } |
| |
| for (ChannelMap::iterator it = channel_map_.begin(); it != channel_map_.end(); |
| ++it) { |
| it->second->SetVoiceChannel(-1, sync_interface); |
| } |
| if (voice_sync_interface_) { |
| voice_sync_interface_->Release(); |
| } |
| voice_engine_ = voice_engine; |
| voice_sync_interface_ = sync_interface; |
| return 0; |
| } |
| |
| int ViEChannelManager::ConnectVoiceChannel(int channel_id, |
| int audio_channel_id) { |
| CriticalSectionScoped cs(*channel_id_critsect_); |
| if (!voice_sync_interface_) { |
| WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id), |
| "No VoE set"); |
| return -1; |
| } |
| ViEChannel* channel = ViEChannelPtr(channel_id); |
| if (!channel) { |
| return -1; |
| } |
| return channel->SetVoiceChannel(audio_channel_id, voice_sync_interface_); |
| } |
| |
| int ViEChannelManager::DisconnectVoiceChannel(int channel_id) { |
| CriticalSectionScoped cs(*channel_id_critsect_); |
| ViEChannel* channel = ViEChannelPtr(channel_id); |
| if (channel) { |
| channel->SetVoiceChannel(-1, NULL); |
| return 0; |
| } |
| return -1; |
| } |
| |
| VoiceEngine* ViEChannelManager::GetVoiceEngine() { |
| CriticalSectionScoped cs(*channel_id_critsect_); |
| return voice_engine_; |
| } |
| |
| bool ViEChannelManager::SetRembStatus(int channel_id, bool sender, |
| bool receiver) { |
| CriticalSectionScoped cs(*channel_id_critsect_); |
| ViEChannel* channel = ViEChannelPtr(channel_id); |
| if (!channel) { |
| return false; |
| } |
| |
| ViEEncoder* encoder = ViEEncoderPtr(channel_id); |
| |
| if (sender || receiver) { |
| if (!channel->EnableRemb(true)) { |
| return false; |
| } |
| } else { |
| channel->EnableRemb(false); |
| } |
| RtpRtcp* rtp_module = channel->rtp_rtcp(); |
| if (sender) { |
| remb_->AddRembSender(rtp_module); |
| remb_->AddSendChannel(encoder->SendRtpRtcpModule()); |
| } else { |
| remb_->RemoveRembSender(rtp_module); |
| remb_->RemoveSendChannel(encoder->SendRtpRtcpModule()); |
| } |
| if (receiver) { |
| remb_->AddReceiveChannel(rtp_module); |
| } else { |
| remb_->RemoveReceiveChannel(rtp_module); |
| } |
| if (sender || receiver) { |
| rtp_module->SetRemoteBitrateObserver(remb_.get()); |
| } else { |
| rtp_module->SetRemoteBitrateObserver(NULL); |
| } |
| return true; |
| } |
| |
| ViEChannel* ViEChannelManager::ViEChannelPtr(int channel_id) const { |
| CriticalSectionScoped cs(*channel_id_critsect_); |
| ChannelMap::const_iterator it = channel_map_.find(channel_id); |
| if (it == channel_map_.end()) { |
| WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_), |
| "%s Channel doesn't exist: %d", __FUNCTION__, channel_id); |
| return NULL; |
| } |
| return it->second; |
| } |
| |
| void ViEChannelManager::GetViEChannels(MapWrapper& channel_map) { |
| CriticalSectionScoped cs(*channel_id_critsect_); |
| if (channel_map.Size() == 0) { |
| return; |
| } |
| |
| // Add all items to 'channelMap'. |
| for (ChannelMap::iterator it = channel_map_.begin(); it != channel_map_.end(); |
| ++it) { |
| channel_map.Insert(it->first, it->second); |
| } |
| } |
| |
| ViEEncoder* ViEChannelManager::ViEEncoderPtr(int video_channel_id) const { |
| CriticalSectionScoped cs(*channel_id_critsect_); |
| EncoderMap::const_iterator it = vie_encoder_map_.find(video_channel_id); |
| if (it == vie_encoder_map_.end()) { |
| return NULL; |
| } |
| return it->second; |
| } |
| |
| bool ViEChannelManager::GetFreeChannelId(int& free_channel_id) { |
| CriticalSectionScoped cs(*channel_id_critsect_); |
| int idx = 0; |
| while (idx < free_channel_ids_size_) { |
| if (free_channel_ids_[idx] == true) { |
| // We've found a free id, allocate it and return. |
| free_channel_ids_[idx] = false; |
| free_channel_id = idx + kViEChannelIdBase; |
| return true; |
| } |
| idx++; |
| } |
| // No free channel id. |
| free_channel_id = -1; |
| return false; |
| } |
| |
| void ViEChannelManager::ReturnChannelId(int channel_id) { |
| CriticalSectionScoped cs(*channel_id_critsect_); |
| assert(channel_id < kViEMaxNumberOfChannels + kViEChannelIdBase && |
| channel_id >= kViEChannelIdBase); |
| free_channel_ids_[channel_id - kViEChannelIdBase] = true; |
| } |
| |
| bool ViEChannelManager::ChannelUsingViEEncoder(int channel_id) const { |
| CriticalSectionScoped cs(*channel_id_critsect_); |
| EncoderMap::const_iterator orig_it = vie_encoder_map_.find(channel_id); |
| if(orig_it == vie_encoder_map_.end()) { |
| // No ViEEncoder for this channel. |
| return false; |
| } |
| |
| // Loop through all other channels to see if anyone points at the same |
| // ViEEncoder. |
| for (EncoderMap::const_iterator comp_it = vie_encoder_map_.begin(); |
| comp_it != vie_encoder_map_.end(); ++comp_it) { |
| // Make sure we're not comparing the same channel with itself. |
| if (comp_it->first != channel_id) { |
| if (comp_it->second == orig_it->second) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| void ViEChannelManager::ChannelsUsingViEEncoder(int channel_id, |
| ChannelList* channels) const { |
| CriticalSectionScoped cs(*channel_id_critsect_); |
| EncoderMap::const_iterator orig_it = vie_encoder_map_.find(channel_id); |
| |
| for (ChannelMap::const_iterator c_it = channel_map_.begin(); |
| c_it != channel_map_.end(); ++c_it) { |
| EncoderMap::const_iterator comp_it = vie_encoder_map_.find(c_it->first); |
| assert(comp_it != vie_encoder_map_.end()); |
| if (comp_it->second == orig_it->second) { |
| channels->push_back(c_it->second); |
| } |
| } |
| } |
| |
| ViEChannelManagerScoped::ViEChannelManagerScoped( |
| const ViEChannelManager& vie_channel_manager) |
| : ViEManagerScopedBase(vie_channel_manager) { |
| } |
| |
| ViEChannel* ViEChannelManagerScoped::Channel(int vie_channel_id) const { |
| return static_cast<const ViEChannelManager*>(vie_manager_)->ViEChannelPtr( |
| vie_channel_id); |
| } |
| ViEEncoder* ViEChannelManagerScoped::Encoder(int vie_channel_id) const { |
| return static_cast<const ViEChannelManager*>(vie_manager_)->ViEEncoderPtr( |
| vie_channel_id); |
| } |
| |
| bool ViEChannelManagerScoped::ChannelUsingViEEncoder(int channel_id) const { |
| return (static_cast<const ViEChannelManager*>(vie_manager_))-> |
| ChannelUsingViEEncoder(channel_id); |
| } |
| |
| void ViEChannelManagerScoped::ChannelsUsingViEEncoder( |
| int channel_id, ChannelList* channels) const { |
| (static_cast<const ViEChannelManager*>(vie_manager_))-> |
| ChannelsUsingViEEncoder(channel_id, channels); |
| } |
| |
| } // namespace webrtc |