LCOV - code coverage report
Current view: top level - src/plugin - callservicesmanager.cpp (source / functions) Coverage Total Hit
Test: jami-coverage-filtered.info Lines: 84.1 % 145 122
Test Date: 2026-11-09 10:41:14 Functions: 95.2 % 21 20

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

Generated by: LCOV version 2.0-1