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