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 : #pragma once
18 :
19 : #ifdef HAVE_CONFIG_H
20 : #include "config.h"
21 : #endif
22 :
23 : #include "logger.h"
24 :
25 : #include "conference.h"
26 : #include "media/recordable.h"
27 : #include "media/peerrecorder.h"
28 : #include "media/media_codec.h"
29 : #include "media/media_attribute.h"
30 :
31 : #include <dhtnet/ip_utils.h>
32 :
33 : #include <atomic>
34 : #include <mutex>
35 : #include <map>
36 : #include <sstream>
37 : #include <memory>
38 : #include <vector>
39 : #include <condition_variable>
40 : #include <set>
41 : #include <list>
42 : #include <functional>
43 :
44 : template<typename T>
45 : bool
46 797 : is_uninitialized(std::weak_ptr<T> const& weak)
47 : {
48 : using wt = std::weak_ptr<T>;
49 797 : return !weak.owner_before(wt {}) && !wt {}.owner_before(weak);
50 : }
51 :
52 : namespace jami {
53 :
54 : class VoIPLink;
55 : class Account;
56 : class AudioDeviceGuard;
57 :
58 : class Call;
59 : class Conference;
60 :
61 : using CallMap = std::map<std::string, std::shared_ptr<Call>>;
62 :
63 : namespace video {
64 : class VideoGenerator;
65 : }
66 :
67 : /*
68 : * @file call.h
69 : * @brief A call is the base class for protocol-based calls
70 : */
71 :
72 : class Call : public Recordable, public PeerRecorder, public std::enable_shared_from_this<Call>
73 : {
74 : public:
75 : /**
76 : * Tell where we're at with the call. The call gets Connected when we know
77 : * from the other end what happened with out call. A call can be 'Connected'
78 : * even if the call state is Busy, or Error.
79 : *
80 : * Audio should be transmitted when ConnectionState = Connected AND
81 : * CallState = Active.
82 : *
83 : * \note modify validStateTransition/getStateStr if this enum changes
84 : */
85 : enum class ConnectionState : unsigned {
86 : DISCONNECTED,
87 : TRYING,
88 : PROGRESSING,
89 : RINGING,
90 : CONNECTED,
91 : COUNT__
92 : };
93 :
94 : /**
95 : * The Call State.
96 : *
97 : * \note modify validStateTransition/getStateStr if this enum changes
98 : */
99 : enum class CallState : unsigned {
100 : INACTIVE,
101 : ACTIVE,
102 : HOLD,
103 : BUSY,
104 : PEER_BUSY,
105 : MERROR,
106 : OVER,
107 : COUNT__
108 : };
109 :
110 : enum class LinkType { GENERIC, SIP };
111 :
112 : using SubcallSet = std::set<std::shared_ptr<Call>, std::owner_less<std::shared_ptr<Call>>>;
113 : using OnReadyCb = std::function<void(bool)>;
114 : using StateListenerCb = std::function<bool(CallState, ConnectionState, int)>;
115 :
116 : /**
117 : * This determines if the call originated from the local user (OUTGOING)
118 : * or from some remote peer (INCOMING, MISSED).
119 : */
120 : enum class CallType : unsigned { INCOMING, OUTGOING, MISSED };
121 :
122 : virtual ~Call();
123 :
124 2526 : std::weak_ptr<Call> weak() { return std::static_pointer_cast<Call>(shared_from_this()); }
125 :
126 0 : virtual LinkType getLinkType() const { return LinkType::GENERIC; }
127 :
128 : /**
129 : * Return a reference on the call id
130 : * @return call id
131 : */
132 23137 : const std::string& getCallId() const { return id_; }
133 :
134 : /**
135 : * Return a reference on the conference id
136 : * @return call id
137 : */
138 452 : std::shared_ptr<Conference> getConference() const { return conf_.lock(); }
139 797 : bool isConferenceParticipant() const { return not is_uninitialized(conf_); }
140 :
141 3224 : std::weak_ptr<Account> getAccount() const { return account_; }
142 : std::string getAccountId() const;
143 :
144 189 : CallType getCallType() const { return type_; }
145 :
146 : /**
147 : * Set the peer number (destination on outgoing)
148 : * not protected by mutex (when created)
149 : * @param number peer number
150 : */
151 414 : void setPeerNumber(const std::string& number) { peerNumber_ = number; }
152 :
153 : /**
154 : * Get the peer number (destination on outgoing)
155 : * not protected by mutex (when created)
156 : * @return std::string The peer number
157 : */
158 2161 : const std::string& getPeerNumber() const { return peerNumber_; }
159 : /**
160 : * Set the display name (caller in ingoing)
161 : * not protected by mutex (when created)
162 : * @return std::string The peer display name
163 : */
164 107 : void setPeerDisplayName(const std::string& name) { peerDisplayName_ = name; }
165 :
166 : /**
167 : * Get "To" from the invite
168 : * @note Used to make the difference between incoming calls for accounts and for conversations
169 : * @return the "To" that was present in the invite
170 : */
171 675 : const std::string& toUsername() const { return toUsername_; }
172 : /**
173 : * Updated by sipvoiplink, corresponds to the "To" in the invite
174 : * @param username "To"
175 : */
176 107 : void toUsername(const std::string& username) { toUsername_ = username; }
177 :
178 : /**
179 : * Get the peer display name (caller in ingoing)
180 : * not protected by mutex (when created)
181 : * @return std::string The peer name
182 : */
183 : const std::string& getPeerDisplayName() const { return peerDisplayName_; }
184 : /**
185 : * Tell if the call is incoming
186 : * @return true if yes false otherwise
187 : */
188 1491 : bool isIncoming() const { return type_ == CallType::INCOMING; }
189 :
190 : /**
191 : * Set the state of the call (protected by mutex)
192 : * @param call_state The call state
193 : * @param cnx_state The call connection state
194 : * @param code Optional error-dependent code (used to report more information)
195 : * @return true if the requested state change was valid, false otherwise
196 : */
197 : bool setState(CallState call_state, signed code = 0);
198 : bool setState(CallState call_state, ConnectionState cnx_state, signed code = 0);
199 : bool setState(ConnectionState cnx_state, signed code = 0);
200 :
201 : /**
202 : * Get the call state of the call (protected by mutex)
203 : * @return CallState The call state
204 : */
205 : CallState getState() const;
206 :
207 : /**
208 : * Get the connection state of the call (protected by mutex)
209 : * @return ConnectionState The connection state
210 : */
211 : ConnectionState getConnectionState() const;
212 :
213 : std::string getStateStr() const;
214 :
215 : void setIPToIP(bool IPToIP) { isIPToIP_ = IPToIP; }
216 :
217 : virtual std::map<std::string, std::string> getDetails() const;
218 :
219 : /**
220 : * Answer the call
221 : */
222 : virtual void answer() = 0;
223 :
224 : /**
225 : * Answer a call with a list of media attributes.
226 : * @param mediaList The list of the media attributes.
227 : * The media attributes set by the caller of this method will
228 : * determine the response sent to the peer and the configuration
229 : * of the local media.
230 : * If the media list is empty, the current media set when the call
231 : * was created will be used.
232 : */
233 : virtual void answer(const std::vector<libjami::MediaMap>& mediaList) = 0;
234 :
235 : /**
236 : * Check the media of an incoming media change request.
237 : * This method checks the new media against the current media. It
238 : * determines if the differences are significant enough to require
239 : * more processing.
240 : * For instance, this can be used to check if the a change request
241 : * must be reported to the client for confirmation or can be handled
242 : * by the daemon.
243 : * The conditions that cause this method to return true are implementation
244 : * specific.
245 : *
246 : * @param the new media list from the remote
247 : * @return true if the new media differs from the current media
248 : **/
249 : virtual bool checkMediaChangeRequest(const std::vector<libjami::MediaMap>& remoteMediaList) = 0;
250 :
251 : /**
252 : * Process incoming media change request.
253 : *
254 : * @param the new media list from the remote
255 : */
256 : virtual void handleMediaChangeRequest(const std::vector<libjami::MediaMap>& remoteMediaList) = 0;
257 :
258 : /**
259 : * Answer to a media update request.
260 : * The media attributes set by the caller of this method will
261 : * determine the response to send to the peer and the configuration
262 : * of the local media.
263 : * @param mediaList The list of media attributes. An empty media
264 : * list means the media update request was not accepted, meaning the
265 : * call continue with the current media. It's up to the implementation
266 : * to determine wether an answer will be sent to the peer.
267 : * @param isRemote True if the media list is from the remote peer
268 : */
269 : virtual void answerMediaChangeRequest(const std::vector<libjami::MediaMap>& mediaList,
270 : bool isRemote = false)
271 : = 0;
272 : /**
273 : * Hang up the call
274 : * @param reason
275 : */
276 : virtual void hangup(int reason) = 0;
277 :
278 : /**
279 : * Refuse incoming call
280 : */
281 : virtual void refuse() = 0;
282 :
283 : /**
284 : * Transfer a call to specified URI
285 : * @param to The recipient of the call
286 : */
287 : virtual void transfer(const std::string& to) = 0;
288 :
289 : /**
290 : * Attended transfer
291 : * @param The target call id
292 : * @return True on success
293 : */
294 : virtual bool attendedTransfer(const std::string& to) = 0;
295 :
296 : /**
297 : * Put a call on hold
298 : * @param cb On hold can be queued if waiting for ICE. This callback will be called when ready
299 : * @return bool True on success, False if failed or pending
300 : */
301 : virtual bool onhold(OnReadyCb&& cb) = 0;
302 :
303 : /**
304 : * Resume a call from hold state
305 : * @param cb On hold can be queued if waiting for ICE. This callback will be called when ready
306 : * @return bool True on success, False if failed or pending
307 : */
308 : virtual bool offhold(OnReadyCb&& cb) = 0;
309 :
310 : virtual void sendKeyframe(int streamIdx = -1) = 0;
311 :
312 : /**
313 : * Check wether ICE is enabled for media
314 : */
315 : virtual bool isIceEnabled() const = 0;
316 :
317 : /**
318 : * Peer has hung up a call
319 : */
320 : virtual void peerHungup();
321 :
322 : virtual void removeCall();
323 :
324 : /**
325 : * Update recording state. Typically used to send notifications
326 : * to peers about the local recording session state
327 : */
328 : virtual void updateRecState(bool state) = 0;
329 :
330 673 : void addStateListener(StateListenerCb&& listener)
331 : {
332 673 : std::lock_guard lk {callMutex_};
333 673 : stateChangedListeners_.emplace_back(std::move(listener));
334 673 : }
335 :
336 : /**
337 : * Attach subcall to this instance.
338 : * If this subcall is answered, this subcall and this instance will be merged using merge().
339 : */
340 : void addSubCall(Call& call);
341 :
342 : ///
343 : /// Return true if this call instance is a subcall (internal call for multi-device handling)
344 : ///
345 4234 : bool isSubcall() const
346 : {
347 4234 : std::lock_guard lk {callMutex_};
348 8468 : return parent_ != nullptr;
349 4234 : }
350 :
351 : /**
352 : * @return Call duration in milliseconds
353 : */
354 297 : std::chrono::milliseconds getCallDuration() const
355 : {
356 297 : return duration_start_ == time_point::min()
357 33 : ? std::chrono::milliseconds::zero()
358 264 : : std::chrono::duration_cast<std::chrono::milliseconds>(clock::now()
359 594 : - duration_start_);
360 : }
361 :
362 : // media management
363 : virtual bool toggleRecording();
364 :
365 : virtual std::vector<MediaAttribute> getMediaAttributeList() const = 0;
366 :
367 : virtual std::map<std::string, bool> getAudioStreams() const = 0;
368 :
369 : #ifdef ENABLE_VIDEO
370 : virtual void createSinks(ConfInfo& infos) = 0;
371 : #endif
372 :
373 0 : virtual void switchInput(const std::string& = {}) {};
374 :
375 : /**
376 : * mute/unmute a media of a call
377 : * @param mediaType type of media
378 : * @param isMuted true for muting, false for unmuting
379 : */
380 : virtual void muteMedia(const std::string& mediaType, bool isMuted) = 0;
381 :
382 : /**
383 : * Send DTMF
384 : * @param code The char code
385 : */
386 : virtual void carryingDTMFdigits(char code) = 0;
387 :
388 : /**
389 : * Make a change request of the current media with the provided media
390 : * @param mediaList the new media list
391 : * @return true on success
392 : */
393 : virtual bool requestMediaChange(const std::vector<libjami::MediaMap>& mediaList) = 0;
394 :
395 : /**
396 : * Retrieve current medias list
397 : * @return current medias
398 : */
399 : virtual std::vector<libjami::MediaMap> currentMediaList() const = 0;
400 :
401 : /**
402 : * Send a message to a call identified by its callid
403 : *
404 : * @param A list of mimetype/payload pairs
405 : * @param The sender of this message (could be another participant of a conference)
406 : */
407 : virtual void sendTextMessage(const std::map<std::string, std::string>& messages,
408 : const std::string& from)
409 : = 0;
410 :
411 : void onTextMessage(std::map<std::string, std::string>&& messages);
412 :
413 0 : virtual std::shared_ptr<SystemCodecInfo> getAudioCodec() const
414 : {
415 0 : return {};
416 : }
417 0 : virtual std::shared_ptr<SystemCodecInfo> getVideoCodec() const
418 : {
419 0 : return {};
420 : }
421 :
422 : virtual void restartMediaSender() = 0;
423 :
424 : // Media status methods
425 : virtual bool hasVideo() const = 0;
426 : virtual bool isCaptureDeviceMuted(const MediaType& mediaType) const = 0;
427 :
428 : /**
429 : * A Call can be in a conference. If this is the case, the other side
430 : * will send conference information describing the rendered image
431 : * @msg A JSON object describing the conference
432 : */
433 : void setConferenceInfo(const std::string& msg);
434 :
435 : virtual void enterConference(std::shared_ptr<Conference> conference) = 0;
436 : virtual void exitConference() = 0;
437 :
438 0 : std::vector<std::map<std::string, std::string>> getConferenceInfos() const
439 : {
440 0 : return confInfo_.toVectorMapStringString();
441 : }
442 :
443 : std::unique_ptr<AudioDeviceGuard> audioGuard;
444 : void sendConfOrder(const Json::Value& root);
445 : void sendConfInfo(const std::string& json);
446 : void resetConfInfo();
447 :
448 : virtual void monitor() const = 0;
449 :
450 6 : int conferenceProtocolVersion() const
451 : {
452 6 : return peerConfProtocol_;
453 : }
454 :
455 : protected:
456 : using clock = std::chrono::steady_clock;
457 : using time_point = clock::time_point;
458 : virtual void merge(Call& scall);
459 :
460 : /**
461 : * Constructor of a call
462 : * @param id Unique identifier of the call
463 : * @param type set definitely this call as incoming/outgoing
464 : * @param details volatile details to customize the call creation
465 : */
466 : Call(const std::shared_ptr<Account>& account,
467 : const std::string& id,
468 : Call::CallType type,
469 : const std::map<std::string, std::string>& details = {});
470 :
471 : // TODO all these members are not protected against multi-thread access
472 :
473 : const std::string id_ {};
474 :
475 : ///< MultiDevice: parent call, nullptr otherwise. Access protected by callMutex_.
476 : mutable std::shared_ptr<Call> parent_;
477 :
478 : ///< MultiDevice: list of attached subcall
479 : SubcallSet subcalls_;
480 :
481 : using MsgList = std::list<std::pair<std::map<std::string, std::string>, std::string>>;
482 :
483 : ///< MultiDevice: message waiting to be sent (need a valid subcall)
484 : MsgList pendingOutMessages_;
485 :
486 : /** Protect every attribute that can be changed by two threads */
487 : mutable std::recursive_mutex callMutex_ {};
488 :
489 : mutable std::mutex confInfoMutex_ {};
490 : mutable ConfInfo confInfo_ {};
491 : time_point duration_start_ {time_point::min()};
492 :
493 : private:
494 : bool validStateTransition(CallState newState);
495 :
496 : void checkPendingIM();
497 :
498 : void checkAudio();
499 :
500 : void subcallStateChanged(Call&, Call::CallState, Call::ConnectionState);
501 :
502 : SubcallSet safePopSubcalls();
503 :
504 : std::vector<StateListenerCb> stateChangedListeners_ {};
505 :
506 : protected:
507 : /** Unique conference ID, used exclusively in case of a conference */
508 : std::weak_ptr<Conference> conf_ {};
509 :
510 : /** Type of the call */
511 : CallType type_;
512 :
513 : /** Associate account ID */
514 : std::weak_ptr<Account> account_;
515 :
516 : /** Disconnected/Progressing/Trying/Ringing/Connected */
517 : ConnectionState connectionState_ {ConnectionState::DISCONNECTED};
518 :
519 : /** Inactive/Active/Hold/Busy/Error */
520 : CallState callState_ {CallState::INACTIVE};
521 :
522 : std::string reason_ {};
523 :
524 : /** Direct IP-to-IP or classic call */
525 : bool isIPToIP_ {false};
526 :
527 : /** Number of the peer */
528 : std::string peerNumber_ {};
529 :
530 : /** Peer Display Name */
531 : std::string peerDisplayName_ {};
532 :
533 : time_t timestamp_start_ {0};
534 :
535 : ///< MultiDevice: message received by subcall to merged yet
536 : MsgList pendingInMessages_;
537 :
538 : /// Supported conference protocol version
539 : int peerConfProtocol_ {0};
540 : std::string toUsername_ {};
541 : };
542 :
543 : // Helpers
544 :
545 : /**
546 : * Obtain a shared smart pointer of instance
547 : */
548 : inline std::shared_ptr<Call>
549 937 : getPtr(Call& call)
550 : {
551 937 : return call.shared_from_this();
552 : }
553 :
554 : } // namespace jami
|