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-04-25 08:05:53 Functions: 20 21 95.2 %

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

Generated by: LCOV version 1.14