LCOV - code coverage report
Current view: top level - src/plugin - callservicesmanager.cpp (source / functions) Coverage Total Hit
Test: jami-coverage-filtered.info Lines: 17.6 % 142 25
Test Date: 2026-06-13 09:18:46 Functions: 30.4 % 23 7

            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           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          733 : CallServicesManager::createAVSubject(const StreamData& data, AVSubjectSPtr subject)
      44              : {
      45         1068 :     auto predicate = [&data](const std::pair<const StreamData, AVSubjectSPtr>& item) {
      46         1068 :         return data.id == item.first.id && data.direction == item.first.direction && data.type == item.first.type;
      47          733 :     };
      48          733 :     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          733 :     callAVsubjects_[data.id].emplace_back(data, subject);
      53              : 
      54              :     // Search for activation flag.
      55          733 :     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          733 : }
      82              : 
      83              : void
      84          452 : CallServicesManager::clearAVSubject(const std::string& callId)
      85              : {
      86          452 :     callAVsubjects_.erase(callId);
      87          452 : }
      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            0 :                                       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           96 :     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_ERROR("Error toggling media handler: {}", 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          423 : CallServicesManager::clearCallHandlerMaps(const std::string& callId)
     229              : {
     230          423 :     mediaHandlerToggled_.erase(callId);
     231          423 : }
     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 2.0-1