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