summaryrefslogtreecommitdiff
path: root/video_engine/vie_channel_manager.cc
diff options
context:
space:
mode:
authorandrew@webrtc.org <andrew@webrtc.org@4adac7df-926f-26a2-2b94-8c16560cd09d>2012-10-22 18:19:23 +0000
committerandrew@webrtc.org <andrew@webrtc.org@4adac7df-926f-26a2-2b94-8c16560cd09d>2012-10-22 18:19:23 +0000
commitb015cbede88899f67a53fbbe581b02ce8e327949 (patch)
tree530a64a3cfdbbabacab974c183326517d49e761e /video_engine/vie_channel_manager.cc
downloadwebrtc-b015cbede88899f67a53fbbe581b02ce8e327949.tar.gz
Move src/ -> webrtc/
TBR=niklas.enbom@webrtc.org Review URL: https://webrtc-codereview.appspot.com/915006 git-svn-id: http://webrtc.googlecode.com/svn/trunk/webrtc@2963 4adac7df-926f-26a2-2b94-8c16560cd09d
Diffstat (limited to 'video_engine/vie_channel_manager.cc')
-rw-r--r--video_engine/vie_channel_manager.cc551
1 files changed, 551 insertions, 0 deletions
diff --git a/video_engine/vie_channel_manager.cc b/video_engine/vie_channel_manager.cc
new file mode 100644
index 00000000..9fabd8d4
--- /dev/null
+++ b/video_engine/vie_channel_manager.cc
@@ -0,0 +1,551 @@
+/*
+ * 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" // NOLINT
+#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/encoder_state_feedback.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/include/voe_video_sync.h"
+
+namespace webrtc {
+
+ViEChannelManager::ViEChannelManager(
+ int engine_id,
+ int number_of_cores,
+ ViEPerformanceMonitor* vie_performance_monitor,
+ const OverUseDetectorOptions& options)
+ : channel_id_critsect_(CriticalSectionWrapper::CreateCriticalSection()),
+ engine_id_(engine_id),
+ number_of_cores_(number_of_cores),
+ free_channel_ids_(new bool[kViEMaxNumberOfChannels]),
+ free_channel_ids_size_(kViEMaxNumberOfChannels),
+ voice_sync_interface_(NULL),
+ voice_engine_(NULL),
+ module_process_thread_(NULL),
+ over_use_detector_options_(options),
+ bwe_mode_(RemoteBitrateEstimator::kSingleStreamEstimation) {
+ 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_);
+
+ 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;
+ }
+ assert(channel_groups_.empty());
+ assert(channel_map_.empty());
+ assert(vie_encoder_map_.empty());
+}
+
+void ViEChannelManager::SetModuleProcessThread(
+ ProcessThread* module_process_thread) {
+ assert(!module_process_thread_);
+ module_process_thread_ = module_process_thread;
+}
+
+int ViEChannelManager::CreateChannel(int* channel_id) {
+ CriticalSectionScoped cs(channel_id_critsect_);
+
+ // Get a new channel id.
+ int new_channel_id = FreeChannelId();
+ if (new_channel_id == -1) {
+ return -1;
+ }
+
+ // Create a new channel group and add this channel.
+ ChannelGroup* group = new ChannelGroup(module_process_thread_,
+ over_use_detector_options_,
+ bwe_mode_);
+ BitrateController* bitrate_controller = group->GetBitrateController();
+ ViEEncoder* vie_encoder = new ViEEncoder(engine_id_, new_channel_id,
+ number_of_cores_,
+ *module_process_thread_,
+ bitrate_controller);
+
+ RtcpBandwidthObserver* bandwidth_observer =
+ bitrate_controller->CreateRtcpBandwidthObserver();
+ RemoteBitrateEstimator* remote_bitrate_estimator =
+ group->GetRemoteBitrateEstimator();
+ EncoderStateFeedback* encoder_state_feedback =
+ group->GetEncoderStateFeedback();
+
+ if (!(vie_encoder->Init() &&
+ CreateChannelObject(new_channel_id, vie_encoder, bandwidth_observer,
+ remote_bitrate_estimator,
+ encoder_state_feedback->GetRtcpIntraFrameObserver(),
+ true))) {
+ delete vie_encoder;
+ vie_encoder = NULL;
+ ReturnChannelId(new_channel_id);
+ delete group;
+ return -1;
+ }
+
+ // Add ViEEncoder to EncoderFeedBackObserver.
+ unsigned int ssrc = 0;
+ int idx = 0;
+ channel_map_[new_channel_id]->GetLocalSSRC(idx, &ssrc);
+ encoder_state_feedback->AddEncoder(ssrc, vie_encoder);
+ std::list<unsigned int> ssrcs;
+ ssrcs.push_back(ssrc);
+ vie_encoder->SetSsrcs(ssrcs);
+
+ *channel_id = new_channel_id;
+ group->AddChannel(*channel_id);
+ channel_groups_.push_back(group);
+ return 0;
+}
+
+int ViEChannelManager::CreateChannel(int* channel_id,
+ int original_channel,
+ bool sender) {
+ CriticalSectionScoped cs(channel_id_critsect_);
+
+ ChannelGroup* channel_group = FindGroup(original_channel);
+ if (!channel_group) {
+ return -1;
+ }
+
+ int new_channel_id = FreeChannelId();
+ if (new_channel_id == -1) {
+ return -1;
+ }
+
+ BitrateController* bitrate_controller = channel_group->GetBitrateController();
+
+ RtcpBandwidthObserver* bandwidth_observer =
+ bitrate_controller->CreateRtcpBandwidthObserver();
+ RemoteBitrateEstimator* remote_bitrate_estimator =
+ channel_group->GetRemoteBitrateEstimator();
+ EncoderStateFeedback* encoder_state_feedback =
+ channel_group->GetEncoderStateFeedback();
+
+ ViEEncoder* vie_encoder = NULL;
+ if (sender) {
+ // We need to create a new ViEEncoder.
+ vie_encoder = new ViEEncoder(engine_id_, new_channel_id, number_of_cores_,
+ *module_process_thread_,
+ bitrate_controller);
+ if (!(vie_encoder->Init() &&
+ CreateChannelObject(
+ new_channel_id, vie_encoder, bandwidth_observer,
+ remote_bitrate_estimator,
+ encoder_state_feedback->GetRtcpIntraFrameObserver(), sender))) {
+ delete vie_encoder;
+ vie_encoder = NULL;
+ }
+ // Register the ViEEncoder to get key frame requests for this channel.
+ unsigned int ssrc = 0;
+ int stream_idx = 0;
+ channel_map_[new_channel_id]->GetLocalSSRC(stream_idx, &ssrc);
+ encoder_state_feedback->AddEncoder(ssrc, vie_encoder);
+ } else {
+ vie_encoder = ViEEncoderPtr(original_channel);
+ assert(vie_encoder);
+ if (!CreateChannelObject(
+ new_channel_id, vie_encoder, bandwidth_observer,
+ remote_bitrate_estimator,
+ encoder_state_feedback->GetRtcpIntraFrameObserver(), sender)) {
+ vie_encoder = NULL;
+ }
+ }
+
+ if (!vie_encoder) {
+ ReturnChannelId(new_channel_id);
+ return -1;
+ }
+
+ *channel_id = new_channel_id;
+ channel_group->AddChannel(*channel_id);
+ return 0;
+}
+
+int ViEChannelManager::DeleteChannel(int channel_id) {
+ ViEChannel* vie_channel = NULL;
+ ViEEncoder* vie_encoder = NULL;
+ ChannelGroup* group = NULL;
+ {
+ // Write lock to make sure no one is using the channel.
+ ViEManagerWriteScoped wl(this);
+
+ // Protect the maps.
+ 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);
+
+ 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;
+
+ group = FindGroup(channel_id);
+ group->SetChannelRembStatus(channel_id, false, false, vie_channel,
+ vie_encoder);
+ group->GetEncoderStateFeedback()->RemoveEncoder(vie_encoder);
+
+ unsigned int remote_ssrc = 0;
+ vie_channel->GetRemoteSSRC(&remote_ssrc);
+ group->RemoveChannel(channel_id, remote_ssrc);
+
+ // Check if other channels are using the same encoder.
+ if (ChannelUsingViEEncoder(channel_id)) {
+ vie_encoder = NULL;
+ } else {
+ // 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);
+
+ if (group->Empty()) {
+ channel_groups_.remove(group);
+ } else {
+ group = NULL; // Prevent group from being deleted.
+ }
+ }
+ delete vie_channel;
+ // 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 statment just to show that this object is not always deleted.
+ if (vie_encoder) {
+ WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_),
+ "%s ViEEncoder deleted for channel %d", __FUNCTION__,
+ channel_id);
+ delete vie_encoder;
+ }
+ // If statment just to show that this object is not always deleted.
+ if (group) {
+ // Delete the group if empty last since the encoder holds a pointer to the
+ // BitrateController object that the group owns.
+ WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_),
+ "%s ChannelGroup deleted for channel %d", __FUNCTION__,
+ channel_id);
+ delete group;
+ }
+ 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_);
+ ChannelGroup* group = FindGroup(channel_id);
+ if (!group) {
+ return false;
+ }
+ ViEChannel* channel = ViEChannelPtr(channel_id);
+ assert(channel);
+ ViEEncoder* encoder = ViEEncoderPtr(channel_id);
+ assert(encoder);
+
+ return group->SetChannelRembStatus(channel_id, sender, receiver, channel,
+ encoder);
+}
+
+bool ViEChannelManager::SetBandwidthEstimationMode(
+ BandwidthEstimationMode mode) {
+ CriticalSectionScoped cs(channel_id_critsect_);
+ if (channel_groups_.size() > 0) {
+ return false;
+ }
+ switch (mode) {
+ case kViEMultiStreamEstimation:
+ bwe_mode_ = RemoteBitrateEstimator::kMultiStreamEstimation;
+ break;
+ case kViESingleStreamEstimation:
+ bwe_mode_ = RemoteBitrateEstimator::kSingleStreamEstimation;
+ break;
+ default:
+ assert(false);
+ return false;
+ }
+ return true;
+}
+
+void ViEChannelManager::UpdateSsrcs(int channel_id,
+ const std::list<unsigned int>& ssrcs) {
+ CriticalSectionScoped cs(channel_id_critsect_);
+ ChannelGroup* channel_group = FindGroup(channel_id);
+ if (channel_group == NULL) {
+ return;
+ }
+ ViEEncoder* encoder = ViEEncoderPtr(channel_id);
+ assert(encoder);
+
+ EncoderStateFeedback* encoder_state_feedback =
+ channel_group->GetEncoderStateFeedback();
+ for (std::list<unsigned int>::const_iterator it = ssrcs.begin();
+ it != ssrcs.end(); ++it) {
+ encoder_state_feedback->AddEncoder(*it, encoder);
+ }
+}
+
+bool ViEChannelManager::CreateChannelObject(
+ int channel_id,
+ ViEEncoder* vie_encoder,
+ RtcpBandwidthObserver* bandwidth_observer,
+ RemoteBitrateEstimator* remote_bitrate_estimator,
+ RtcpIntraFrameObserver* intra_frame_observer,
+ bool sender) {
+ // Register the channel at the encoder.
+ RtpRtcp* send_rtp_rtcp_module = vie_encoder->SendRtpRtcpModule();
+
+ ViEChannel* vie_channel = new ViEChannel(channel_id, engine_id_,
+ number_of_cores_,
+ *module_process_thread_,
+ intra_frame_observer,
+ bandwidth_observer,
+ remote_bitrate_estimator,
+ send_rtp_rtcp_module,
+ sender);
+ if (vie_channel->Init() != 0) {
+ WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_),
+ "%s could not init channel", __FUNCTION__, channel_id);
+ delete vie_channel;
+ return false;
+ }
+ VideoCodec encoder;
+ if (vie_encoder->GetEncoder(&encoder) != 0) {
+ WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id),
+ "%s: Could not GetEncoder.", __FUNCTION__);
+ delete vie_channel;
+ return false;
+ }
+ if (sender && vie_channel->SetSendCodec(encoder) != 0) {
+ WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id),
+ "%s: Could not SetSendCodec.", __FUNCTION__);
+ delete vie_channel;
+ return false;
+ }
+ // Store the channel, add it to the channel group and save the vie_encoder.
+ channel_map_[channel_id] = vie_channel;
+ vie_encoder_map_[channel_id] = vie_encoder;
+ 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;
+}
+
+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;
+}
+
+int ViEChannelManager::FreeChannelId() {
+ 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;
+ return idx + kViEChannelIdBase;
+ }
+ idx++;
+ }
+ WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_),
+ "Max number of channels reached: %d", channel_map_.size());
+ return -1;
+}
+
+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;
+}
+
+ChannelGroup* ViEChannelManager::FindGroup(int channel_id) {
+ for (ChannelGroups::iterator it = channel_groups_.begin();
+ it != channel_groups_.end(); ++it) {
+ if ((*it)->HasChannel(channel_id)) {
+ return *it;
+ }
+ }
+ return NULL;
+}
+
+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