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
|