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