| /* |
| * Copyright (c) 2011 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. |
| */ |
| |
| /* |
| * vie_channel_manager.cc |
| */ |
| |
| #include "vie_channel_manager.h" |
| #include "engine_configurations.h" |
| #include "vie_defines.h" |
| |
| #include "critical_section_wrapper.h" |
| #include "trace.h" |
| #include "vie_channel.h" |
| #include "vie_encoder.h" |
| #include "process_thread.h" |
| |
| // VoiceEngine |
| #include "voe_video_sync.h" |
| |
| namespace webrtc |
| { |
| |
| ViEChannelManagerScoped::ViEChannelManagerScoped( |
| const ViEChannelManager& vieChannelManager) |
| : ViEManagerScopedBase(vieChannelManager) |
| { |
| } |
| |
| ViEChannel* ViEChannelManagerScoped::Channel(int vieChannelId) const |
| { |
| return static_cast<const ViEChannelManager*> |
| (_vieManager)->ViEChannelPtr(vieChannelId); |
| } |
| ViEEncoder* ViEChannelManagerScoped::Encoder(int vieChannelId) const |
| { |
| return static_cast<const ViEChannelManager*> |
| (_vieManager)->ViEEncoderPtr(vieChannelId); |
| } |
| |
| bool ViEChannelManagerScoped::ChannelUsingViEEncoder(int channelId) const |
| { |
| return (static_cast<const ViEChannelManager*> |
| (_vieManager))->ChannelUsingViEEncoder( channelId); |
| } |
| |
| // ============================================================================ |
| // VieChannelManager |
| // ============================================================================ |
| |
| // ---------------------------------------------------------------------------- |
| // Constructor |
| // ---------------------------------------------------------------------------- |
| |
| ViEChannelManager::ViEChannelManager(int engineId, |
| int numberOfCores, |
| ViEPerformanceMonitor& viePerformanceMonitor) |
| : _ptrChannelIdCritsect(CriticalSectionWrapper::CreateCriticalSection()), |
| _engineId(engineId), _numberOfCores(numberOfCores), |
| _viePerformanceMonitor(viePerformanceMonitor), _channelMap(), |
| _freeChannelIds(new bool[kViEMaxNumberOfChannels]), |
| _freeChannelIdsSize(kViEMaxNumberOfChannels), _vieEncoderMap(), |
| _voiceSyncInterface(NULL), _voiceEngine(NULL), |
| _moduleProcessThread(NULL) |
| { |
| WEBRTC_TRACE(webrtc::kTraceMemory, webrtc::kTraceVideo, ViEId(engineId), |
| "ViEChannelManager::ViEChannelManager(engineId: %d) - Constructor", |
| engineId); |
| |
| for (int idx = 0; idx < _freeChannelIdsSize; idx++) |
| { |
| _freeChannelIds[idx] = true; |
| } |
| } |
| // ---------------------------------------------------------------------------- |
| // SetModuleProcessThread |
| // Initialize the thread context used by none time critical tasks in video channels. |
| // ---------------------------------------------------------------------------- |
| void ViEChannelManager::SetModuleProcessThread( ProcessThread& moduleProcessThread) |
| { |
| assert(!_moduleProcessThread); |
| _moduleProcessThread = &moduleProcessThread; |
| } |
| |
| // ---------------------------------------------------------------------------- |
| // Destructor |
| // ---------------------------------------------------------------------------- |
| |
| ViEChannelManager::~ViEChannelManager() |
| { |
| WEBRTC_TRACE(webrtc::kTraceMemory, webrtc::kTraceVideo, ViEId(_engineId), |
| "ViEChannelManager Destructor, engineId: %d", _engineId); |
| |
| while (_channelMap.Size() != 0) |
| { |
| MapItem* item = _channelMap.First(); |
| const int channelId = item->GetId(); |
| item = NULL; |
| DeleteChannel(channelId); |
| } |
| |
| if (_voiceSyncInterface) |
| _voiceSyncInterface->Release(); |
| if (_ptrChannelIdCritsect) |
| { |
| delete _ptrChannelIdCritsect; |
| _ptrChannelIdCritsect = NULL; |
| } |
| if (_freeChannelIds) |
| { |
| delete[] _freeChannelIds; |
| _freeChannelIds = NULL; |
| _freeChannelIdsSize = 0; |
| } |
| } |
| |
| // ---------------------------------------------------------------------------- |
| // CreateChannel |
| // |
| // Creates a new channel. 'channelId' will be the id of the created channel |
| // ---------------------------------------------------------------------------- |
| int ViEChannelManager::CreateChannel(int& channelId) |
| { |
| CriticalSectionScoped cs(*_ptrChannelIdCritsect); |
| |
| // Get a free id for the new channel |
| if (GetFreeChannelId(channelId) == false) |
| { |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(_engineId), |
| "Max number of channels reached: %d", _channelMap.Size()); |
| return -1; |
| } |
| |
| ViEChannel* vieChannel = new ViEChannel(channelId, _engineId, |
| _numberOfCores, |
| *_moduleProcessThread); |
| if (vieChannel == NULL) |
| { |
| ReturnChannelId(channelId); |
| return -1; |
| } |
| if (vieChannel->Init() != 0) |
| { |
| // Could not init channel |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(_engineId), |
| "%s could not init channel", __FUNCTION__, channelId); |
| ReturnChannelId(channelId); |
| delete vieChannel; |
| vieChannel = NULL; |
| return -1; |
| |
| } |
| // There is no ViEEncoder for this channel, create one with default settings |
| ViEEncoder* vieEncoder = new ViEEncoder(_engineId, channelId, |
| _numberOfCores, |
| *_moduleProcessThread); |
| if (vieEncoder == NULL) |
| { |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(_engineId), |
| "%s(videoChannelId: %d) - Could not create a new encoder", |
| __FUNCTION__, channelId); |
| delete vieChannel; |
| return -1; |
| } |
| |
| // Add to the map |
| if (_vieEncoderMap.Insert(channelId, vieEncoder) != 0) |
| { |
| // Could not add to the map |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(_engineId), |
| "%s: Could not add new encoder for video channel %d", |
| __FUNCTION__, channelId); |
| delete vieChannel; |
| delete vieEncoder; |
| return -1; |
| } |
| _channelMap.Insert(channelId, vieChannel); |
| // Register the channel at the encoder |
| RtpRtcp* ptrSendRtpRtcpModule = vieEncoder->SendRtpRtcpModule(); |
| if (vieChannel->RegisterSendRtpRtcpModule(*ptrSendRtpRtcpModule) != 0) |
| { |
| assert(false); |
| _vieEncoderMap.Erase(channelId); |
| _channelMap.Erase(channelId); |
| ReturnChannelId(channelId); |
| delete vieChannel; |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(_engineId, channelId), |
| "%s: Could not register rtp module %d", __FUNCTION__, |
| channelId); |
| return -1; |
| } |
| return 0; |
| } |
| |
| // ---------------------------------------------------------------------------- |
| // CreateChannel |
| // |
| // Creates a channel and attaches to an already existing ViEEncoder |
| // ---------------------------------------------------------------------------- |
| |
| int ViEChannelManager::CreateChannel(int& channelId, int originalChannel) |
| { |
| CriticalSectionScoped cs(*_ptrChannelIdCritsect); |
| |
| // Check that originalChannel already exists |
| ViEEncoder* vieEncoder = ViEEncoderPtr(originalChannel); |
| if (vieEncoder == NULL) |
| { |
| // The original channel doesn't exist |
| WEBRTC_TRACE(webrtc::kTraceError, |
| webrtc::kTraceVideo, |
| ViEId(_engineId), |
| "%s: Original channel doesn't exist", |
| __FUNCTION__, |
| originalChannel); |
| return -1; |
| } |
| VideoCodec videoCodec; |
| if (vieEncoder->GetEncoder(videoCodec) == 0) |
| { |
| if (videoCodec.numberOfSimulcastStreams > 0) |
| { |
| WEBRTC_TRACE(webrtc::kTraceError, |
| webrtc::kTraceVideo, |
| ViEId(_engineId, originalChannel), |
| "%s: Can't share a simulcast encoder", |
| __FUNCTION__); |
| return -1; |
| } |
| } |
| |
| // Get a free id for the new channel |
| if (GetFreeChannelId(channelId) == false) |
| { |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(_engineId), |
| "Max number of channels reached: %d", _channelMap.Size()); |
| return -1; |
| } |
| ViEChannel* vieChannel = new ViEChannel(channelId, _engineId, |
| _numberOfCores, |
| *_moduleProcessThread); |
| if (vieChannel == NULL) |
| { |
| ReturnChannelId(channelId); |
| return -1; |
| } |
| if (vieChannel->Init() != 0) |
| { |
| // Could not init channel |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(_engineId), |
| "%s could not init channel", __FUNCTION__, channelId); |
| ReturnChannelId(channelId); |
| delete vieChannel; |
| vieChannel = NULL; |
| return -1; |
| } |
| if (_vieEncoderMap.Insert(channelId, vieEncoder) != 0) |
| { |
| // Could not add to the map |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(_engineId), |
| "%s: Could not add new encoder for video channel %d", |
| __FUNCTION__, channelId); |
| ReturnChannelId(channelId); |
| delete vieChannel; |
| return -1; |
| } |
| |
| // Set the same encoder settings for the channel as used by the master channel. |
| // Do this before attaching rtp module to ensure all rtp cihldren has the same codec type |
| VideoCodec encoder; |
| if (vieEncoder->GetEncoder(encoder) == 0) |
| { |
| vieChannel->SetSendCodec(encoder); |
| } |
| _channelMap.Insert(channelId, vieChannel); |
| |
| // Register the channel at the encoder |
| RtpRtcp* ptrSendRtpRtcpModule = vieEncoder->SendRtpRtcpModule(); |
| if (vieChannel->RegisterSendRtpRtcpModule(*ptrSendRtpRtcpModule) != 0) |
| { |
| assert(false); |
| _vieEncoderMap.Erase(channelId); |
| _channelMap.Erase(channelId); |
| ReturnChannelId(channelId); |
| delete vieChannel; |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(_engineId, channelId), |
| "%s: Could not register rtp module %d", __FUNCTION__, |
| channelId); |
| return -1; |
| } |
| return 0; |
| } |
| |
| // ---------------------------------------------------------------------------- |
| // DeleteChannel |
| // ---------------------------------------------------------------------------- |
| |
| int ViEChannelManager::DeleteChannel(int channelId) |
| { |
| ViEChannel* vieChannel = NULL; |
| ViEEncoder* vieEncoder = NULL; |
| { |
| // Write lock to make sure no one is using the channel |
| ViEManagerWriteScoped wl(*this); |
| |
| // Protect the map |
| CriticalSectionScoped cs(*_ptrChannelIdCritsect); |
| |
| MapItem* mapItem = _channelMap.Find(channelId); |
| if (mapItem == NULL) |
| { |
| // No such channel |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(_engineId), |
| "%s Channel doesn't exist: %d", __FUNCTION__, channelId); |
| return -1; |
| } |
| vieChannel = reinterpret_cast<ViEChannel*> (mapItem->GetItem()); |
| _channelMap.Erase(mapItem); |
| // Deregister the channel from the ViEEncoder to stop the media flow |
| vieChannel->DeregisterSendRtpRtcpModule(); |
| ReturnChannelId(channelId); |
| |
| // Find the encoder object |
| mapItem = _vieEncoderMap.Find(channelId); |
| if (mapItem == NULL) |
| { |
| assert(false); |
| WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, ViEId(_engineId), |
| "%s ViEEncoder not found for channel %d", __FUNCTION__, |
| channelId); |
| return -1; |
| } |
| // Get the ViEEncoder item |
| vieEncoder = reinterpret_cast<ViEEncoder*> (mapItem->GetItem()); |
| |
| // Check if other channels are using the same encoder |
| if (ChannelUsingViEEncoder(channelId)) |
| { |
| // Don't delete the ViEEncoder, at least on other channel is using it. |
| WEBRTC_TRACE( |
| webrtc::kTraceInfo, |
| webrtc::kTraceVideo, |
| ViEId(_engineId), |
| "%s ViEEncoder removed from map for channel %d, not deleted", |
| __FUNCTION__, channelId); |
| vieEncoder = NULL; |
| } else |
| { |
| WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, ViEId(_engineId), |
| "%s ViEEncoder deleted for channel %d", __FUNCTION__, |
| channelId); |
| // Delete later when we've released the critsect |
| } |
| // We can't erase the item before we've checked for other channels using same ViEEncoder |
| _vieEncoderMap.Erase(mapItem); |
| |
| } |
| // Leave the write critsect before deleting the objects. |
| // Deleting a channel can cause other objects, such as renderers, to be deleted and might take time |
| if (vieEncoder) |
| { |
| delete vieEncoder; |
| } |
| delete vieChannel; |
| |
| WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideo, ViEId(_engineId), |
| "%s Channel %d deleted", __FUNCTION__, channelId); |
| return 0; |
| } |
| |
| // ---------------------------------------------------------------------------- |
| // Channel |
| // |
| // Returns a pointer to the channel with id 'channelId' |
| // ---------------------------------------------------------------------------- |
| |
| ViEChannel* ViEChannelManager::ViEChannelPtr(int channelId) const |
| { |
| CriticalSectionScoped cs(*_ptrChannelIdCritsect); |
| MapItem* mapItem = _channelMap.Find(channelId); |
| if (mapItem == NULL) |
| { |
| // No such channel |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(_engineId), |
| "%s Channel doesn't exist: %d", __FUNCTION__, channelId); |
| return NULL; |
| } |
| ViEChannel* vieChannel = reinterpret_cast<ViEChannel*> (mapItem->GetItem()); |
| return vieChannel; |
| } |
| |
| // ---------------------------------------------------------------------------- |
| // GetChannels |
| // |
| // Adds all channels to channelMap |
| // ---------------------------------------------------------------------------- |
| |
| void ViEChannelManager::GetViEChannels(MapWrapper& channelMap) |
| { |
| CriticalSectionScoped cs(*_ptrChannelIdCritsect); |
| if (channelMap.Size() == 0) |
| { |
| // No channels |
| return; |
| } |
| // Add all items to 'channelMap' |
| for (MapItem* item = _channelMap.First(); item != NULL; item |
| = _channelMap.Next(item)) |
| { |
| channelMap.Insert(item->GetId(), item->GetItem()); |
| } |
| return; |
| } |
| |
| // ---------------------------------------------------------------------------- |
| // ViEEncoderPtr |
| // |
| // Gets the ViEEncoder used as input for videoChannelId |
| // ---------------------------------------------------------------------------- |
| |
| ViEEncoder* ViEChannelManager::ViEEncoderPtr(int videoChannelId) const |
| { |
| CriticalSectionScoped cs(*_ptrChannelIdCritsect); |
| MapItem* mapItem = _vieEncoderMap.Find(videoChannelId); |
| if (mapItem == NULL) |
| { |
| // No ViEEncoder for this channel... |
| return NULL; |
| } |
| ViEEncoder* vieEncoder = static_cast<ViEEncoder*> (mapItem->GetItem()); |
| return vieEncoder; |
| } |
| |
| // ---------------------------------------------------------------------------- |
| // GetFreeChannelId |
| // |
| // Returns true if we found a new channel id, freeChannelId, false otherwise |
| // ---------------------------------------------------------------------------- |
| bool ViEChannelManager::GetFreeChannelId(int& freeChannelId) |
| { |
| CriticalSectionScoped cs(*_ptrChannelIdCritsect); |
| int idx = 0; |
| while (idx < _freeChannelIdsSize) |
| { |
| if (_freeChannelIds[idx] == true) |
| { |
| // We've found a free id, allocate it and return |
| _freeChannelIds[idx] = false; |
| freeChannelId = idx + kViEChannelIdBase; |
| return true; |
| } |
| idx++; |
| } |
| // No free channel id |
| freeChannelId = -1; |
| return false; |
| } |
| |
| // ---------------------------------------------------------------------------- |
| // ReturnChannelID |
| // |
| // Returns a previously allocated channel id |
| // ---------------------------------------------------------------------------- |
| void ViEChannelManager::ReturnChannelId(int channelId) |
| { |
| CriticalSectionScoped cs(*_ptrChannelIdCritsect); |
| assert(channelId < kViEMaxNumberOfChannels+kViEChannelIdBase && channelId>=kViEChannelIdBase); |
| _freeChannelIds[channelId - kViEChannelIdBase] = true; |
| } |
| |
| // ---------------------------------------------------------------------------- |
| // ChannelUsingViEEncoder |
| // |
| // Returns true if at least one nother channel is using the same encoder |
| // ---------------------------------------------------------------------------- |
| |
| bool ViEChannelManager::ChannelUsingViEEncoder(int channelId) const |
| { |
| CriticalSectionScoped cs(*_ptrChannelIdCritsect); |
| MapItem* channelItem = _vieEncoderMap.Find(channelId); |
| if (channelItem == NULL) |
| { |
| // No ViEEncoder for this channel... |
| return false; |
| } |
| ViEEncoder* channelEncoder = |
| static_cast<ViEEncoder*> (channelItem->GetItem()); |
| |
| // Loop through all other channels to see if anyone points at the same ViEEncoder |
| MapItem* mapItem = _vieEncoderMap.First(); |
| while (mapItem) |
| { |
| if (mapItem->GetId() != channelId) |
| { |
| if (channelEncoder == static_cast<ViEEncoder*> (mapItem->GetItem())) |
| { |
| // We've found another channel using the same ViEEncoder |
| return true; |
| } |
| } |
| mapItem = _vieEncoderMap.Next(mapItem); |
| } |
| return false; |
| } |
| |
| // ---------------------------------------------------------------------------- |
| // SetVoiceEngine |
| // |
| // Set the voice engine instance to be used by all video channels. We are interested in the voice engine sync interfaces |
| // ---------------------------------------------------------------------------- |
| int ViEChannelManager::SetVoiceEngine(VoiceEngine* voiceEngine) |
| { |
| |
| // Write lock to make sure no one is using the channel |
| ViEManagerWriteScoped wl(*this); |
| |
| CriticalSectionScoped cs(*_ptrChannelIdCritsect); |
| |
| VoEVideoSync* syncInterface = NULL; |
| if (voiceEngine) |
| { |
| // Get new sync interface; |
| syncInterface = VoEVideoSync::GetInterface(voiceEngine); |
| if (!syncInterface) |
| { |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(_engineId), |
| "%s Can't get audio sync interface from VoiceEngine.", |
| __FUNCTION__); |
| |
| if (syncInterface) |
| { |
| syncInterface->Release(); |
| } |
| return -1; |
| } |
| } |
| |
| for (MapItem* item = _channelMap.First(); item != NULL; item |
| = _channelMap.Next(item)) |
| { |
| ViEChannel* channel = static_cast<ViEChannel*> (item->GetItem()); |
| assert(channel); |
| channel->SetVoiceChannel(-1, syncInterface); |
| } |
| if (_voiceSyncInterface) |
| { |
| _voiceSyncInterface->Release(); |
| } |
| _voiceEngine = voiceEngine; |
| _voiceSyncInterface = syncInterface; |
| return 0; |
| |
| } |
| VoiceEngine* ViEChannelManager::GetVoiceEngine() |
| { |
| CriticalSectionScoped cs(*_ptrChannelIdCritsect); |
| return _voiceEngine; |
| |
| } |
| |
| // ---------------------------------------------------------------------------- |
| // ConnectVoiceChannel |
| // |
| // Enables lip sync of the channel. |
| // ---------------------------------------------------------------------------- |
| int ViEChannelManager::ConnectVoiceChannel(int channelId, int audioChannelId) |
| { |
| CriticalSectionScoped cs(*_ptrChannelIdCritsect); |
| |
| if (_voiceSyncInterface == NULL) |
| { |
| WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo, ViEId(_engineId, channelId), |
| "No VoE set"); |
| return -1; |
| } |
| ViEChannel* channel = ViEChannelPtr(channelId); |
| if (!channel) |
| { |
| return -1; |
| } |
| return channel->SetVoiceChannel(audioChannelId, _voiceSyncInterface); |
| |
| } |
| |
| // ---------------------------------------------------------------------------- |
| // DisconnectVoiceChannel |
| // |
| // Disables lip sync of the channel. |
| // ---------------------------------------------------------------------------- |
| int ViEChannelManager::DisconnectVoiceChannel(int channelId) |
| { |
| CriticalSectionScoped cs(*_ptrChannelIdCritsect); |
| ViEChannel* channel = ViEChannelPtr(channelId); |
| if (channel) |
| { |
| channel->SetVoiceChannel(-1, NULL); |
| return 0; |
| } else |
| { |
| return -1; |
| } |
| } |
| } // namespace webrtc |