LCOV - code coverage report
Current view: top level - foo/src/plugin - callservicesmanager.cpp (source / functions) Hit Total Coverage
Test: jami-coverage-filtered.info Lines: 25 141 17.7 %
Date: 2025-12-18 10:07:43 Functions: 7 21 33.3 %

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

Generated by: LCOV version 1.14