Line data Source code
1 : /*
2 : * Copyright (C) 2020-2024 Savoir-faire Linux Inc.
3 : *
4 : * Author: Aline Gondim Santos <aline.gondimsantos@savoirfairelinux.com>
5 : *
6 : * This program is free software; you can redistribute it and/or modify
7 : * it under the terms of the GNU General Public License as published by
8 : * the Free Software Foundation; either version 3 of the License, or
9 : * (at your option) any later version.
10 : *
11 : * This program is distributed in the hope that it will be useful,
12 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : * GNU General Public License for more details.
15 : *
16 : * You should have received a copy of the GNU General Public License
17 : * along with this program; if not, write to the Free Software
18 : * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 : */
20 :
21 : #include "callservicesmanager.h"
22 :
23 : #include "pluginmanager.h"
24 : #include "pluginpreferencesutils.h"
25 :
26 : #include "manager.h"
27 : #include "sip/sipcall.h"
28 : #include "fileutils.h"
29 : #include "logger.h"
30 :
31 : namespace jami {
32 :
33 33 : CallServicesManager::CallServicesManager(PluginManager& pluginManager)
34 : {
35 33 : registerComponentsLifeCycleManagers(pluginManager);
36 33 : }
37 :
38 33 : CallServicesManager::~CallServicesManager()
39 : {
40 33 : callMediaHandlers_.clear();
41 33 : callAVsubjects_.clear();
42 33 : mediaHandlerToggled_.clear();
43 33 : }
44 :
45 : void
46 782 : CallServicesManager::createAVSubject(const StreamData& data, AVSubjectSPtr subject)
47 : {
48 2601 : auto predicate = [&data](std::pair<const StreamData, AVSubjectSPtr> item) {
49 2208 : return data.id == item.first.id && data.direction == item.first.direction
50 2208 : && data.type == item.first.type;
51 782 : };
52 782 : callAVsubjects_[data.id].remove_if(predicate);
53 :
54 : // callAVsubjects_ emplaces data and subject with callId key to easy of access
55 : // When call is ended, subjects from this call are erased.
56 782 : callAVsubjects_[data.id].emplace_back(data, subject);
57 :
58 : // Search for activation flag.
59 798 : for (auto& callMediaHandler : callMediaHandlers_) {
60 16 : std::size_t found = callMediaHandler->id().find_last_of(DIR_SEPARATOR_CH);
61 : // toggle is true if we should automatically activate the MediaHandler.
62 16 : bool toggle = PluginPreferencesUtils::getAlwaysPreference(
63 32 : callMediaHandler->id().substr(0, found),
64 32 : callMediaHandler->getCallMediaHandlerDetails().at("name"),
65 16 : data.source);
66 : // toggle may be overwritten if the MediaHandler was previously activated/deactivated
67 : // for the given call.
68 16 : for (const auto& toggledMediaHandlerPair : mediaHandlerToggled_[data.id]) {
69 0 : if (toggledMediaHandlerPair.first == (uintptr_t) callMediaHandler.get()) {
70 0 : toggle = toggledMediaHandlerPair.second;
71 0 : break;
72 : }
73 : }
74 16 : if (toggle)
75 : #ifndef __ANDROID__
76 : // If activation is expected, we call activation function
77 0 : toggleCallMediaHandler((uintptr_t) callMediaHandler.get(), data.id, true);
78 : #else
79 : // Due to Android's camera activation process, we don't automaticaly
80 : // activate the MediaHandler here. But we set it as active
81 : // and the client-android will handle its activation.
82 : mediaHandlerToggled_[data.id].insert({(uintptr_t) callMediaHandler.get(), true});
83 : #endif
84 : }
85 782 : }
86 :
87 : void
88 485 : CallServicesManager::clearAVSubject(const std::string& callId)
89 : {
90 485 : callAVsubjects_.erase(callId);
91 485 : }
92 :
93 : void
94 33 : CallServicesManager::registerComponentsLifeCycleManagers(PluginManager& pluginManager)
95 : {
96 : // registerMediaHandler may be called by the PluginManager upon loading a plugin.
97 32 : auto registerMediaHandler = [this](void* data, std::mutex& pmMtx_) {
98 16 : std::lock_guard lk(pmMtx_);
99 16 : CallMediaHandlerPtr ptr {(static_cast<CallMediaHandler*>(data))};
100 :
101 16 : if (!ptr)
102 0 : return -1;
103 16 : std::size_t found = ptr->id().find_last_of(DIR_SEPARATOR_CH);
104 : // Adding preference that tells us to automatically activate a MediaHandler.
105 16 : PluginPreferencesUtils::addAlwaysHandlerPreference(ptr->getCallMediaHandlerDetails().at(
106 : "name"),
107 32 : ptr->id().substr(0, found));
108 16 : callMediaHandlers_.emplace_back(std::move(ptr));
109 16 : return 0;
110 16 : };
111 :
112 : // unregisterMediaHandler may be called by the PluginManager while unloading.
113 96 : auto unregisterMediaHandler = [this](void* data, std::mutex& pmMtx_) {
114 16 : std::lock_guard lk(pmMtx_);
115 16 : auto handlerIt = std::find_if(callMediaHandlers_.begin(),
116 : callMediaHandlers_.end(),
117 32 : [data](CallMediaHandlerPtr& handler) {
118 16 : return (handler.get() == data);
119 : });
120 :
121 16 : if (handlerIt != callMediaHandlers_.end()) {
122 16 : for (auto& toggledList : mediaHandlerToggled_) {
123 0 : auto handlerId = std::find_if(toggledList.second.begin(),
124 : toggledList.second.end(),
125 0 : [handlerIt](
126 : std::pair<uintptr_t, bool> handlerIdPair) {
127 0 : return handlerIdPair.first
128 0 : == (uintptr_t) handlerIt->get()
129 0 : && handlerIdPair.second;
130 : });
131 : // If MediaHandler we're trying to destroy is currently in use, we deactivate it.
132 0 : if (handlerId != toggledList.second.end())
133 0 : toggleCallMediaHandler((*handlerId).first, toggledList.first, false);
134 : }
135 16 : callMediaHandlers_.erase(handlerIt);
136 : }
137 16 : return true;
138 16 : };
139 :
140 : // Services are registered to the PluginManager.
141 33 : pluginManager.registerComponentManager("CallMediaHandlerManager",
142 : registerMediaHandler,
143 : unregisterMediaHandler);
144 33 : }
145 :
146 : std::vector<std::string>
147 2 : CallServicesManager::getCallMediaHandlers()
148 : {
149 2 : std::vector<std::string> res;
150 2 : res.reserve(callMediaHandlers_.size());
151 6 : for (const auto& mediaHandler : callMediaHandlers_) {
152 4 : res.emplace_back(std::to_string((uintptr_t) mediaHandler.get()));
153 : }
154 2 : return res;
155 0 : }
156 :
157 : void
158 4 : CallServicesManager::toggleCallMediaHandler(const std::string& mediaHandlerId,
159 : const std::string& callId,
160 : const bool toggle)
161 : {
162 : try {
163 4 : toggleCallMediaHandler(std::stoull(mediaHandlerId), callId, toggle);
164 0 : } catch (const std::exception& e) {
165 0 : JAMI_ERR("Error toggling media handler: %s", e.what());
166 0 : }
167 4 : }
168 :
169 : std::map<std::string, std::string>
170 4 : CallServicesManager::getCallMediaHandlerDetails(const std::string& mediaHandlerIdStr)
171 : {
172 4 : auto mediaHandlerId = std::stoull(mediaHandlerIdStr);
173 6 : for (auto& mediaHandler : callMediaHandlers_) {
174 6 : if ((uintptr_t) mediaHandler.get() == mediaHandlerId) {
175 4 : return mediaHandler->getCallMediaHandlerDetails();
176 : }
177 : }
178 0 : return {};
179 : }
180 :
181 : bool
182 8 : CallServicesManager::isVideoType(const CallMediaHandlerPtr& mediaHandler)
183 : {
184 : // "dataType" is known from the MediaHandler implementation.
185 8 : const auto& details = mediaHandler->getCallMediaHandlerDetails();
186 8 : const auto& it = details.find("dataType");
187 8 : if (it != details.end()) {
188 : bool status;
189 8 : std::istringstream(it->second) >> status;
190 8 : return status;
191 : }
192 : // If there is no "dataType" returned, it's safer to return True and allow
193 : // sender to restart.
194 0 : return true;
195 8 : }
196 :
197 : bool
198 8 : CallServicesManager::isAttached(const CallMediaHandlerPtr& mediaHandler)
199 : {
200 : // "attached" is known from the MediaHandler implementation.
201 8 : const auto& details = mediaHandler->getCallMediaHandlerDetails();
202 8 : const auto& it = details.find("attached");
203 8 : if (it != details.end()) {
204 : bool status;
205 8 : std::istringstream(it->second) >> status;
206 8 : return status;
207 : }
208 0 : return true;
209 8 : }
210 :
211 : std::vector<std::string>
212 4 : CallServicesManager::getCallMediaHandlerStatus(const std::string& callId)
213 : {
214 4 : std::vector<std::string> ret;
215 4 : const auto& it = mediaHandlerToggled_.find(callId);
216 4 : if (it != mediaHandlerToggled_.end())
217 10 : for (const auto& mediaHandlerId : it->second)
218 6 : if (mediaHandlerId.second) // Only return active MediaHandler ids
219 2 : ret.emplace_back(std::to_string(mediaHandlerId.first));
220 8 : return ret;
221 0 : }
222 :
223 : bool
224 2 : CallServicesManager::setPreference(const std::string& key,
225 : const std::string& value,
226 : const std::string& rootPath)
227 : {
228 2 : bool status {true};
229 2 : for (auto& mediaHandler : callMediaHandlers_) {
230 0 : if (mediaHandler->id().find(rootPath) != std::string::npos) {
231 0 : if (mediaHandler->preferenceMapHasKey(key)) {
232 0 : mediaHandler->setPreferenceAttribute(key, value);
233 0 : status &= false;
234 : }
235 : }
236 : }
237 2 : return status;
238 : }
239 :
240 : void
241 455 : CallServicesManager::clearCallHandlerMaps(const std::string& callId)
242 : {
243 455 : mediaHandlerToggled_.erase(callId);
244 455 : }
245 :
246 : void
247 8 : CallServicesManager::notifyAVSubject(CallMediaHandlerPtr& callMediaHandlerPtr,
248 : const StreamData& data,
249 : AVSubjectSPtr& subject)
250 : {
251 8 : if (auto soSubject = subject.lock())
252 8 : callMediaHandlerPtr->notifyAVFrameSubject(data, soSubject);
253 8 : }
254 :
255 : void
256 4 : CallServicesManager::toggleCallMediaHandler(const uintptr_t mediaHandlerId,
257 : const std::string& callId,
258 : const bool toggle)
259 : {
260 4 : auto& handlers = mediaHandlerToggled_[callId];
261 4 : bool applyRestart = false;
262 :
263 20 : for (auto subject : callAVsubjects_[callId]) {
264 16 : auto handlerIt = std::find_if(callMediaHandlers_.begin(),
265 : callMediaHandlers_.end(),
266 48 : [mediaHandlerId](CallMediaHandlerPtr& handler) {
267 24 : return ((uintptr_t) handler.get() == mediaHandlerId);
268 : });
269 :
270 16 : if (handlerIt != callMediaHandlers_.end()) {
271 16 : if (toggle) {
272 8 : notifyAVSubject((*handlerIt), subject.first, subject.second);
273 8 : if (isAttached((*handlerIt)))
274 6 : handlers[mediaHandlerId] = true;
275 : } else {
276 8 : (*handlerIt)->detach();
277 8 : handlers[mediaHandlerId] = false;
278 : }
279 16 : if (subject.first.type == StreamType::video && isVideoType((*handlerIt)))
280 4 : applyRestart = true;
281 : }
282 16 : }
283 : #ifndef __ANDROID__
284 : #ifdef ENABLE_VIDEO
285 4 : if (applyRestart) {
286 2 : auto call = Manager::instance().callFactory.getCall<SIPCall>(callId);
287 2 : if (call && !call->isConferenceParticipant()) {
288 4 : for (auto const& videoRtp: call->getRtpSessionList(MediaType::MEDIA_VIDEO))
289 4 : videoRtp->restartSender();
290 : }
291 2 : }
292 : #endif
293 : #endif
294 4 : }
295 : } // namespace jami
|