Project 'savoirfairelinux/ring-client-macosx' was moved to 'savoirfairelinux/jami-client-macos'. Please update any links and bookmarks that may still have the old path.
Select Git revision
ringaccount.cpp
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
callservicesmanager.cpp 11.54 KiB
/*
* Copyright (C) 2020-2021 Savoir-faire Linux Inc.
*
* Author: Aline Gondim Santos <aline.gondimsantos@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "callservicesmanager.h"
#include "pluginmanager.h"
#include "pluginpreferencesutils.h"
#include "manager.h"
#include "sip/sipcall.h"
#include "fileutils.h"
#include "logger.h"
namespace jami {
CallServicesManager::CallServicesManager(PluginManager& pluginManager)
{
registerComponentsLifeCycleManagers(pluginManager);
}
CallServicesManager::~CallServicesManager()
{
callMediaHandlers_.clear();
callAVsubjects_.clear();
mediaHandlerToggled_.clear();
}
void
CallServicesManager::createAVSubject(const StreamData& data, AVSubjectSPtr subject)
{
auto predicate = [&data](std::pair<const StreamData, AVSubjectSPtr> item) {
return data.id == item.first.id && data.direction == item.first.direction
&& data.type == item.first.type;
};
callAVsubjects_[data.id].remove_if(predicate);
// callAVsubjects_ emplaces data and subject with callId key to easy of access
// When call is ended, subjects from this call are erased.
callAVsubjects_[data.id].emplace_back(data, subject);
// Search for activation flag.
for (auto& callMediaHandler : callMediaHandlers_) {
std::size_t found = callMediaHandler->id().find_last_of(DIR_SEPARATOR_CH);
// toggle is true if we should automatically activate the MediaHandler.
bool toggle = PluginPreferencesUtils::getAlwaysPreference(
callMediaHandler->id().substr(0, found),
callMediaHandler->getCallMediaHandlerDetails().at("name"));
// toggle may be overwritten if the MediaHandler was previously activated/deactivated
// for the given call.
for (const auto& toggledMediaHandlerPair : mediaHandlerToggled_[data.id]) {
if (toggledMediaHandlerPair.first == (uintptr_t) callMediaHandler.get()) {
toggle = toggledMediaHandlerPair.second;
break;
}
}
if (toggle)
#ifndef __ANDROID__
// If activation is expected, we call activation function
toggleCallMediaHandler((uintptr_t) callMediaHandler.get(), data.id, true);
#else
// Due to Android's camera activation process, we don't automaticaly
// activate the MediaHandler here. But we set it as active
// and the client-android will handle its activation.
mediaHandlerToggled_[data.id].insert({(uintptr_t) callMediaHandler.get(), true});
#endif
}
}
void
CallServicesManager::pauseRestartMediaHandlers(bool isPluginEnabled)
{
auto calls = Manager::instance().callFactory.getAllCalls();
for (auto& call : calls) {
if (call->getState() == Call::CallState::ACTIVE) {
auto callId = call->getCallId();
for (const auto& toggledMediaHandlerPair : mediaHandlerToggled_[callId])
toggleCallMediaHandler(toggledMediaHandlerPair.first, callId, isPluginEnabled);
}
}
}
void
CallServicesManager::clearAVSubject(const std::string& callId)
{
callAVsubjects_.erase(callId);
}
void
CallServicesManager::registerComponentsLifeCycleManagers(PluginManager& pluginManager)
{
// registerMediaHandler may be called by the PluginManager upon loading a plugin.
auto registerMediaHandler = [this](void* data, std::mutex& pmMtx_) {
std::lock_guard<std::mutex> lk(pmMtx_);
CallMediaHandlerPtr ptr {(static_cast<CallMediaHandler*>(data))};
if (!ptr)
return -1;
std::size_t found = ptr->id().find_last_of(DIR_SEPARATOR_CH);
// Adding preference that tells us to automatically activate a MediaHandler.
PluginPreferencesUtils::addAlwaysHandlerPreference(ptr->getCallMediaHandlerDetails().at(
"name"),
ptr->id().substr(0, found));
callMediaHandlers_.emplace_back(std::move(ptr));
return 0;
};
// unregisterMediaHandler may be called by the PluginManager while unloading.
auto unregisterMediaHandler = [this](void* data, std::mutex& pmMtx_) {
std::lock_guard<std::mutex> lk(pmMtx_);
auto handlerIt = std::find_if(callMediaHandlers_.begin(),
callMediaHandlers_.end(),
[data](CallMediaHandlerPtr& handler) {
return (handler.get() == data);
});
if (handlerIt != callMediaHandlers_.end()) {
for (auto& toggledList : mediaHandlerToggled_) {
auto handlerId = std::find_if(toggledList.second.begin(),
toggledList.second.end(),
[this, handlerIt](
std::pair<uintptr_t, bool> handlerIdPair) {
return handlerIdPair.first
== (uintptr_t) handlerIt->get()
&& handlerIdPair.second;
});
// If MediaHandler we're trying to destroy is currently in use, we deactivate it.
if (handlerId != toggledList.second.end())
toggleCallMediaHandler((*handlerId).first, toggledList.first, false);
}
callMediaHandlers_.erase(handlerIt);
}
return true;
};
// Services are registered to the PluginManager.
pluginManager.registerComponentManager("CallMediaHandlerManager",
registerMediaHandler,
unregisterMediaHandler);
}
std::vector<std::string>
CallServicesManager::getCallMediaHandlers()
{
std::vector<std::string> res;
res.reserve(callMediaHandlers_.size());
for (const auto& mediaHandler : callMediaHandlers_) {
res.emplace_back(std::to_string((uintptr_t) mediaHandler.get()));
}
return res;
}
void
CallServicesManager::toggleCallMediaHandler(const std::string& mediaHandlerId,
const std::string& callId,
const bool toggle)
{
try {
toggleCallMediaHandler(std::stoull(mediaHandlerId), callId, toggle);
} catch (const std::exception& e) {
JAMI_ERR("Error toggling media handler: %s", e.what());
}
}
std::map<std::string, std::string>
CallServicesManager::getCallMediaHandlerDetails(const std::string& mediaHandlerIdStr)
{
auto mediaHandlerId = std::stoull(mediaHandlerIdStr);
for (auto& mediaHandler : callMediaHandlers_) {
if ((uintptr_t) mediaHandler.get() == mediaHandlerId) {
return mediaHandler->getCallMediaHandlerDetails();
}
}
return {};
}
bool
CallServicesManager::isVideoType(const CallMediaHandlerPtr& mediaHandler)
{
// "dataType" is known from the MediaHandler implementation.
const auto& details = mediaHandler->getCallMediaHandlerDetails();
const auto& it = details.find("dataType");
if (it != details.end()) {
bool status;
std::istringstream(it->second) >> status;
return status;
}
// If there is no "dataType" returned, it's safer to return True and allow
// sender to restart.
return true;
}
bool
CallServicesManager::isAttached(const CallMediaHandlerPtr& mediaHandler)
{
// "attached" is known from the MediaHandler implementation.
const auto& details = mediaHandler->getCallMediaHandlerDetails();
const auto& it = details.find("attached");
if (it != details.end()) {
bool status;
std::istringstream(it->second) >> status;
return status;
}
return true;
}
std::vector<std::string>
CallServicesManager::getCallMediaHandlerStatus(const std::string& callId)
{
std::vector<std::string> ret;
const auto& it = mediaHandlerToggled_.find(callId);
if (it != mediaHandlerToggled_.end())
for (const auto& mediaHandlerId : it->second)
if (mediaHandlerId.second) // Only return active MediaHandler ids
ret.emplace_back(std::to_string(mediaHandlerId.first));
return ret;
}
bool
CallServicesManager::setPreference(const std::string& key,
const std::string& value,
const std::string& rootPath)
{
bool status {true};
for (auto& mediaHandler : callMediaHandlers_) {
if (mediaHandler->id().find(rootPath) != std::string::npos) {
if (mediaHandler->preferenceMapHasKey(key)) {
mediaHandler->setPreferenceAttribute(key, value);
status &= false;
}
}
}
return status;
}
void
CallServicesManager::clearCallHandlerMaps(const std::string& callId)
{
mediaHandlerToggled_.erase(callId);
}
void
CallServicesManager::notifyAVSubject(CallMediaHandlerPtr& callMediaHandlerPtr,
const StreamData& data,
AVSubjectSPtr& subject)
{
if (auto soSubject = subject.lock())
callMediaHandlerPtr->notifyAVFrameSubject(data, soSubject);
}
void
CallServicesManager::toggleCallMediaHandler(const uintptr_t mediaHandlerId,
const std::string& callId,
const bool toggle)
{
auto& handlers = mediaHandlerToggled_[callId];
bool applyRestart = false;
auto isPluginEnabled = jami::Manager::instance().pluginPreferences.getPluginsEnabled();
for (auto subject : callAVsubjects_[callId]) {
auto handlerIt = std::find_if(callMediaHandlers_.begin(),
callMediaHandlers_.end(),
[mediaHandlerId](CallMediaHandlerPtr& handler) {
return ((uintptr_t) handler.get() == mediaHandlerId);
});
if (handlerIt != callMediaHandlers_.end()) {
if (toggle) {
if (isPluginEnabled)
notifyAVSubject((*handlerIt), subject.first, subject.second);
if (isAttached((*handlerIt)) || !isPluginEnabled)
handlers[mediaHandlerId] = true;
} else {
(*handlerIt)->detach();
if (isPluginEnabled)
handlers[mediaHandlerId] = false;
}
if (subject.first.type == StreamType::video && isVideoType((*handlerIt)))
applyRestart = true;
}
}
#ifndef __ANDROID__
if (applyRestart) {
auto call = Manager::instance().callFactory.getCall<SIPCall>(callId);
if (call && call->getConfId().empty())
call->getVideoRtp().restartSender();
}
#endif
}
} // namespace jami