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