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
|