LCOV - code coverage report
Current view: top level - src/plugin - callservicesmanager.cpp (source / functions) Hit Total Coverage
Test: jami-coverage-filtered.info Lines: 121 145 83.4 %
Date: 2024-12-21 08:56:24 Functions: 20 21 95.2 %

          Line data    Source code
       1             : /*
       2             :  *  Copyright (C) 2004-2024 Savoir-faire Linux Inc.
       3             :  *
       4             :  *  This program is free software: you can redistribute it and/or modify
       5             :  *  it under the terms of the GNU General Public License as published by
       6             :  *  the Free Software Foundation, either version 3 of the License, or
       7             :  *  (at your option) any later version.
       8             :  *
       9             :  *  This program is distributed in the hope that it will be useful,
      10             :  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
      12             :  *  GNU General Public License for more details.
      13             :  *
      14             :  *  You should have received a copy of the GNU General Public License
      15             :  *  along with this program. If not, see <https://www.gnu.org/licenses/>.
      16             :  */
      17             : 
      18             : #include "callservicesmanager.h"
      19             : 
      20             : #include "pluginmanager.h"
      21             : #include "pluginpreferencesutils.h"
      22             : 
      23             : #include "manager.h"
      24             : #include "sip/sipcall.h"
      25             : #include "fileutils.h"
      26             : #include "logger.h"
      27             : 
      28             : namespace jami {
      29             : 
      30          33 : CallServicesManager::CallServicesManager(PluginManager& pluginManager)
      31             : {
      32          33 :     registerComponentsLifeCycleManagers(pluginManager);
      33          33 : }
      34             : 
      35          33 : CallServicesManager::~CallServicesManager()
      36             : {
      37          33 :     callMediaHandlers_.clear();
      38          33 :     callAVsubjects_.clear();
      39          33 :     mediaHandlerToggled_.clear();
      40          33 : }
      41             : 
      42             : void
      43         788 : CallServicesManager::createAVSubject(const StreamData& data, AVSubjectSPtr subject)
      44             : {
      45        2725 :     auto predicate = [&data](std::pair<const StreamData, AVSubjectSPtr> item) {
      46        2314 :         return data.id == item.first.id && data.direction == item.first.direction
      47        2314 :                && data.type == item.first.type;
      48         788 :     };
      49         788 :     callAVsubjects_[data.id].remove_if(predicate);
      50             : 
      51             :     // callAVsubjects_ emplaces data and subject with callId key to easy of access
      52             :     // When call is ended, subjects from this call are erased.
      53         788 :     callAVsubjects_[data.id].emplace_back(data, subject);
      54             : 
      55             :     // Search for activation flag.
      56         804 :     for (auto& callMediaHandler : callMediaHandlers_) {
      57          16 :         std::size_t found = callMediaHandler->id().find_last_of(DIR_SEPARATOR_CH);
      58             :         // toggle is true if we should automatically activate the MediaHandler.
      59          16 :         bool toggle = PluginPreferencesUtils::getAlwaysPreference(
      60          32 :             callMediaHandler->id().substr(0, found),
      61          32 :             callMediaHandler->getCallMediaHandlerDetails().at("name"),
      62          16 :             data.source);
      63             :         // toggle may be overwritten if the MediaHandler was previously activated/deactivated
      64             :         // for the given call.
      65          16 :         for (const auto& toggledMediaHandlerPair : mediaHandlerToggled_[data.id]) {
      66           0 :             if (toggledMediaHandlerPair.first == (uintptr_t) callMediaHandler.get()) {
      67           0 :                 toggle = toggledMediaHandlerPair.second;
      68           0 :                 break;
      69             :             }
      70             :         }
      71          16 :         if (toggle)
      72             : #ifndef __ANDROID__
      73             :             // If activation is expected, we call activation function
      74           0 :             toggleCallMediaHandler((uintptr_t) callMediaHandler.get(), data.id, true);
      75             : #else
      76             :             // Due to Android's camera activation process, we don't automaticaly
      77             :             // activate the MediaHandler here. But we set it as active
      78             :             // and the client-android will handle its activation.
      79             :             mediaHandlerToggled_[data.id].insert({(uintptr_t) callMediaHandler.get(), true});
      80             : #endif
      81             :     }
      82         788 : }
      83             : 
      84             : void
      85         479 : CallServicesManager::clearAVSubject(const std::string& callId)
      86             : {
      87         479 :     callAVsubjects_.erase(callId);
      88         479 : }
      89             : 
      90             : void
      91          33 : CallServicesManager::registerComponentsLifeCycleManagers(PluginManager& pluginManager)
      92             : {
      93             :     // registerMediaHandler may be called by the PluginManager upon loading a plugin.
      94          32 :     auto registerMediaHandler = [this](void* data, std::mutex& pmMtx_) {
      95          16 :         std::lock_guard lk(pmMtx_);
      96          16 :         CallMediaHandlerPtr ptr {(static_cast<CallMediaHandler*>(data))};
      97             : 
      98          16 :         if (!ptr)
      99           0 :             return -1;
     100          16 :         std::size_t found = ptr->id().find_last_of(DIR_SEPARATOR_CH);
     101             :         // Adding preference that tells us to automatically activate a MediaHandler.
     102          16 :         PluginPreferencesUtils::addAlwaysHandlerPreference(ptr->getCallMediaHandlerDetails().at(
     103             :                                                                "name"),
     104          32 :                                                            ptr->id().substr(0, found));
     105          16 :         callMediaHandlers_.emplace_back(std::move(ptr));
     106          16 :         return 0;
     107          16 :     };
     108             : 
     109             :     // unregisterMediaHandler may be called by the PluginManager while unloading.
     110          96 :     auto unregisterMediaHandler = [this](void* data, std::mutex& pmMtx_) {
     111          16 :         std::lock_guard lk(pmMtx_);
     112          16 :         auto handlerIt = std::find_if(callMediaHandlers_.begin(),
     113             :                                       callMediaHandlers_.end(),
     114          32 :                                       [data](CallMediaHandlerPtr& handler) {
     115          16 :                                           return (handler.get() == data);
     116             :                                       });
     117             : 
     118          16 :         if (handlerIt != callMediaHandlers_.end()) {
     119          16 :             for (auto& toggledList : mediaHandlerToggled_) {
     120           0 :                 auto handlerId = std::find_if(toggledList.second.begin(),
     121             :                                               toggledList.second.end(),
     122           0 :                                               [handlerIt](
     123             :                                                   std::pair<uintptr_t, bool> handlerIdPair) {
     124           0 :                                                   return handlerIdPair.first
     125           0 :                                                              == (uintptr_t) handlerIt->get()
     126           0 :                                                          && handlerIdPair.second;
     127             :                                               });
     128             :                 // If MediaHandler is attempting to destroy one which is currently in use, we deactivate it.
     129           0 :                 if (handlerId != toggledList.second.end())
     130           0 :                     toggleCallMediaHandler((*handlerId).first, toggledList.first, false);
     131             :             }
     132          16 :             callMediaHandlers_.erase(handlerIt);
     133             :         }
     134          16 :         return true;
     135          16 :     };
     136             : 
     137             :     // Services are registered to the PluginManager.
     138          33 :     pluginManager.registerComponentManager("CallMediaHandlerManager",
     139             :                                            registerMediaHandler,
     140             :                                            unregisterMediaHandler);
     141          33 : }
     142             : 
     143             : std::vector<std::string>
     144           2 : CallServicesManager::getCallMediaHandlers()
     145             : {
     146           2 :     std::vector<std::string> res;
     147           2 :     res.reserve(callMediaHandlers_.size());
     148           6 :     for (const auto& mediaHandler : callMediaHandlers_) {
     149           4 :         res.emplace_back(std::to_string((uintptr_t) mediaHandler.get()));
     150             :     }
     151           2 :     return res;
     152           0 : }
     153             : 
     154             : void
     155           4 : CallServicesManager::toggleCallMediaHandler(const std::string& mediaHandlerId,
     156             :                                             const std::string& callId,
     157             :                                             const bool toggle)
     158             : {
     159             :     try {
     160           4 :         toggleCallMediaHandler(std::stoull(mediaHandlerId), callId, toggle);
     161           0 :     } catch (const std::exception& e) {
     162           0 :         JAMI_ERR("Error toggling media handler: %s", e.what());
     163           0 :     }
     164           4 : }
     165             : 
     166             : std::map<std::string, std::string>
     167           4 : CallServicesManager::getCallMediaHandlerDetails(const std::string& mediaHandlerIdStr)
     168             : {
     169           4 :     auto mediaHandlerId = std::stoull(mediaHandlerIdStr);
     170           6 :     for (auto& mediaHandler : callMediaHandlers_) {
     171           6 :         if ((uintptr_t) mediaHandler.get() == mediaHandlerId) {
     172           4 :             return mediaHandler->getCallMediaHandlerDetails();
     173             :         }
     174             :     }
     175           0 :     return {};
     176             : }
     177             : 
     178             : bool
     179           8 : CallServicesManager::isVideoType(const CallMediaHandlerPtr& mediaHandler)
     180             : {
     181             :     // "dataType" is known from the MediaHandler implementation.
     182           8 :     const auto& details = mediaHandler->getCallMediaHandlerDetails();
     183           8 :     const auto& it = details.find("dataType");
     184           8 :     if (it != details.end()) {
     185             :         bool status;
     186           8 :         std::istringstream(it->second) >> status;
     187           8 :         return status;
     188             :     }
     189             :     // If there is no "dataType" returned, it's safer to return True and allow
     190             :     // sender to restart.
     191           0 :     return true;
     192           8 : }
     193             : 
     194             : bool
     195           8 : CallServicesManager::isAttached(const CallMediaHandlerPtr& mediaHandler)
     196             : {
     197             :     // "attached" is known from the MediaHandler implementation.
     198           8 :     const auto& details = mediaHandler->getCallMediaHandlerDetails();
     199           8 :     const auto& it = details.find("attached");
     200           8 :     if (it != details.end()) {
     201             :         bool status;
     202           8 :         std::istringstream(it->second) >> status;
     203           8 :         return status;
     204             :     }
     205           0 :     return true;
     206           8 : }
     207             : 
     208             : std::vector<std::string>
     209           4 : CallServicesManager::getCallMediaHandlerStatus(const std::string& callId)
     210             : {
     211           4 :     std::vector<std::string> ret;
     212           4 :     const auto& it = mediaHandlerToggled_.find(callId);
     213           4 :     if (it != mediaHandlerToggled_.end())
     214          10 :         for (const auto& mediaHandlerId : it->second)
     215           6 :             if (mediaHandlerId.second) // Only return active MediaHandler ids
     216           2 :                 ret.emplace_back(std::to_string(mediaHandlerId.first));
     217           8 :     return ret;
     218           0 : }
     219             : 
     220             : bool
     221           2 : CallServicesManager::setPreference(const std::string& key,
     222             :                                    const std::string& value,
     223             :                                    const std::string& rootPath)
     224             : {
     225           2 :     bool status {true};
     226           2 :     for (auto& mediaHandler : callMediaHandlers_) {
     227           0 :         if (mediaHandler->id().find(rootPath) != std::string::npos) {
     228           0 :             if (mediaHandler->preferenceMapHasKey(key)) {
     229           0 :                 mediaHandler->setPreferenceAttribute(key, value);
     230           0 :                 status &= false;
     231             :             }
     232             :         }
     233             :     }
     234           2 :     return status;
     235             : }
     236             : 
     237             : void
     238         446 : CallServicesManager::clearCallHandlerMaps(const std::string& callId)
     239             : {
     240         446 :     mediaHandlerToggled_.erase(callId);
     241         446 : }
     242             : 
     243             : void
     244           8 : CallServicesManager::notifyAVSubject(CallMediaHandlerPtr& callMediaHandlerPtr,
     245             :                                      const StreamData& data,
     246             :                                      AVSubjectSPtr& subject)
     247             : {
     248           8 :     if (auto soSubject = subject.lock())
     249           8 :         callMediaHandlerPtr->notifyAVFrameSubject(data, soSubject);
     250           8 : }
     251             : 
     252             : void
     253           4 : CallServicesManager::toggleCallMediaHandler(const uintptr_t mediaHandlerId,
     254             :                                             const std::string& callId,
     255             :                                             const bool toggle)
     256             : {
     257           4 :     auto& handlers = mediaHandlerToggled_[callId];
     258           4 :     bool applyRestart = false;
     259             : 
     260          20 :     for (auto subject : callAVsubjects_[callId]) {
     261          16 :         auto handlerIt = std::find_if(callMediaHandlers_.begin(),
     262             :                                       callMediaHandlers_.end(),
     263          48 :                                       [mediaHandlerId](CallMediaHandlerPtr& handler) {
     264          24 :                                           return ((uintptr_t) handler.get() == mediaHandlerId);
     265             :                                       });
     266             : 
     267          16 :         if (handlerIt != callMediaHandlers_.end()) {
     268          16 :             if (toggle) {
     269           8 :                 notifyAVSubject((*handlerIt), subject.first, subject.second);
     270           8 :                 if (isAttached((*handlerIt)))
     271           6 :                     handlers[mediaHandlerId] = true;
     272             :             } else {
     273           8 :                 (*handlerIt)->detach();
     274           8 :                 handlers[mediaHandlerId] = false;
     275             :             }
     276          16 :             if (subject.first.type == StreamType::video && isVideoType((*handlerIt)))
     277           4 :                 applyRestart = true;
     278             :         }
     279          16 :     }
     280             : #ifndef __ANDROID__
     281             : #ifdef ENABLE_VIDEO
     282           4 :     if (applyRestart) {
     283           2 :         auto call = Manager::instance().callFactory.getCall<SIPCall>(callId);
     284           2 :         if (call && !call->isConferenceParticipant()) {
     285           4 :             for (auto const& videoRtp: call->getRtpSessionList(MediaType::MEDIA_VIDEO))
     286           4 :                 videoRtp->restartSender();
     287             :         }
     288           2 :     }
     289             : #endif
     290             : #endif
     291           4 : }
     292             : } // namespace jami

Generated by: LCOV version 1.14