Line data Source code
1 : /*
2 : * Copyright (C) 2004-2025 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 "call.h"
24 : #include <dhtnet/ice_transport.h>
25 : #include "connectivity/sip_utils.h"
26 : #include "media/media_codec.h" // for MediaType enum
27 : #include "sip/sdp.h"
28 :
29 : #include "media/rtp_session.h"
30 : #ifdef ENABLE_VIDEO
31 : #include "media/video/video_receive_thread.h"
32 : #include "media/video/video_rtp_session.h"
33 : #endif
34 : #ifdef ENABLE_PLUGIN
35 : #include "plugin/streamdata.h"
36 : #endif
37 : #include "noncopyable.h"
38 :
39 : #include <memory>
40 : #include <optional>
41 :
42 : extern "C" {
43 : #include <pjsip/sip_config.h>
44 : struct pjsip_evsub;
45 : struct pjsip_inv_session;
46 : struct pjmedia_sdp_session;
47 : struct pj_ice_sess_cand;
48 : struct pjsip_rx_data;
49 : }
50 :
51 : namespace dhtnet {
52 : class IceSocket;
53 : namespace upnp {
54 : class Controller;
55 : }
56 : }
57 :
58 : namespace jami {
59 :
60 : class Sdp;
61 : class SIPAccountBase;
62 : class SipTransport;
63 : class AudioRtpSession;
64 :
65 : using IceCandidate = pj_ice_sess_cand;
66 :
67 : /**
68 : * @file sipcall.h
69 : * @brief SIPCall are SIP implementation of a normal Call
70 : */
71 : class SIPCall : public Call
72 : {
73 : private:
74 : using clock = std::chrono::steady_clock;
75 : using time_point = clock::time_point;
76 :
77 : NON_COPYABLE(SIPCall);
78 :
79 : public:
80 : static constexpr LinkType LINK_TYPE = LinkType::SIP;
81 :
82 : struct RtpStream
83 : {
84 : std::shared_ptr<RtpSession> rtpSession_ {};
85 : std::shared_ptr<MediaAttribute> mediaAttribute_ {};
86 : std::shared_ptr<MediaAttribute> remoteMediaAttribute_;
87 : std::unique_ptr<dhtnet::IceSocket> rtpSocket_;
88 : std::unique_ptr<dhtnet::IceSocket> rtcpSocket_;
89 : };
90 :
91 : /**
92 : * Destructor
93 : */
94 : ~SIPCall();
95 :
96 : /**
97 : * Constructor
98 : * @param id The call identifier
99 : * @param type The type of the call (incoming/outgoing)
100 : * @param mediaList A list of medias to include in the call
101 : */
102 : SIPCall(const std::shared_ptr<SIPAccountBase>& account,
103 : const std::string& id,
104 : Call::CallType type,
105 : const std::vector<libjami::MediaMap>& mediaList);
106 :
107 : // Inherited from Call class
108 803 : LinkType getLinkType() const override { return LINK_TYPE; }
109 :
110 : // Override of Call class
111 : private:
112 : void merge(Call& call) override; // not public - only called by Call
113 :
114 : public:
115 : void answer() override;
116 : void answer(const std::vector<libjami::MediaMap>& mediaList) override;
117 : bool checkMediaChangeRequest(const std::vector<libjami::MediaMap>& remoteMediaList) override;
118 : void handleMediaChangeRequest(const std::vector<libjami::MediaMap>& remoteMediaList) override;
119 : void answerMediaChangeRequest(const std::vector<libjami::MediaMap>& mediaList,
120 : bool isRemote = false) override;
121 : void hangup(int reason) override;
122 : void refuse() override;
123 : void transfer(const std::string& to) override;
124 : bool attendedTransfer(const std::string& to) override;
125 : bool onhold(OnReadyCb&& cb) override;
126 : bool offhold(OnReadyCb&& cb) override;
127 : void switchInput(const std::string& resource = {}) override;
128 : void peerHungup() override;
129 : void carryingDTMFdigits(char code) override;
130 : bool requestMediaChange(const std::vector<libjami::MediaMap>& mediaList) override;
131 : std::vector<libjami::MediaMap> currentMediaList() const override;
132 : void sendTextMessage(const std::map<std::string, std::string>& messages,
133 : const std::string& from) override;
134 : void removeCall() override;
135 : void muteMedia(const std::string& mediaType, bool isMuted) override;
136 : std::vector<MediaAttribute> getMediaAttributeList() const override;
137 : std::map<std::string, bool> getAudioStreams() const override;
138 : void restartMediaSender() override;
139 : std::shared_ptr<SystemCodecInfo> getAudioCodec() const override;
140 : std::shared_ptr<SystemCodecInfo> getVideoCodec() const override;
141 : void sendKeyframe(int streamIdx = -1) override;
142 : bool isIceEnabled() const override;
143 : std::map<std::string, std::string> getDetails() const override;
144 : void enterConference(std::shared_ptr<Conference> conference) override;
145 : void exitConference() override;
146 : #ifdef ENABLE_VIDEO
147 : std::mutex sinksMtx_;
148 : void createSinks(ConfInfo& infos) override;
149 : std::map<std::string, std::shared_ptr<video::SinkClient>> callSinksMap_ {};
150 : std::map<std::string, std::string> local2RemoteSinks_ {};
151 : #endif
152 : bool hasVideo() const override;
153 :
154 : // TODO: cleanup this (used by conference + Call::getDetails() (and clients can use this))
155 : bool isCaptureDeviceMuted(const MediaType& mediaType) const override;
156 2084 : bool isSrtpEnabled() const
157 : {
158 2084 : return srtpEnabled_;
159 : }
160 : // End of override of Call class
161 :
162 : // Override of Recordable class
163 : bool toggleRecording() override;
164 : // End of override of Recordable class
165 :
166 : // Override PeerRecorder
167 : void peerRecording(bool state) override;
168 : void peerMuted(bool state, int streamIdx) override;
169 : void peerVoice(bool state) override;
170 : // end override PeerRecorder
171 :
172 : void monitor() const override;
173 :
174 : /**
175 : * Set peer's User-Agent found in the message header
176 : */
177 : void setPeerUaVersion(std::string_view ua);
178 :
179 : /**
180 : * Set peer's allowed methods
181 : */
182 : void setPeerAllowMethods(std::vector<std::string> methods);
183 :
184 : /**
185 : * Check if a SIP method is allowed by peer
186 : */
187 : bool isSipMethodAllowedByPeer(const std::string_view method) const;
188 :
189 : /**
190 : * Return the SDP's manager of this call
191 : */
192 711 : Sdp& getSDP()
193 : {
194 711 : return *sdp_;
195 : }
196 :
197 : // Implementation of events reported by SipVoipLink.
198 : /**
199 : * Call is in ringing state on peer's side
200 : */
201 : void onPeerRinging();
202 : /**
203 : * Peer answered the call
204 : */
205 : void onAnswered();
206 : /**
207 : * Called to report server/internal errors
208 : * @param cause Optional error code
209 : */
210 : void onFailure(signed cause = 0);
211 : /**
212 : * Peer answered busy
213 : */
214 : void onBusyHere();
215 : /**
216 : * Peer closed the connection
217 : */
218 : void onClosed();
219 :
220 : pj_status_t onReceiveReinvite(const pjmedia_sdp_session* offer, pjsip_rx_data* rdata);
221 : void onReceiveOfferIn200OK(const pjmedia_sdp_session* offer);
222 :
223 : /**
224 : * Called when the media negotiation (SDP offer/answer) has
225 : * completed.
226 : */
227 : void onMediaNegotiationComplete();
228 : // End fo SiPVoipLink events
229 :
230 : const std::string& getContactHeader() const;
231 :
232 : void setSipTransport(const std::shared_ptr<SipTransport>& transport,
233 : const std::string& contactHdr = {});
234 :
235 591 : SipTransport* getTransport()
236 : {
237 591 : return sipTransport_.get();
238 : }
239 :
240 : void sendSIPInfo(std::string_view body, std::string_view subtype);
241 :
242 : void requestKeyframe(int streamIdx = -1);
243 :
244 : void updateRecState(bool state) override;
245 :
246 : std::shared_ptr<SIPAccountBase> getSIPAccount() const;
247 :
248 : bool remoteHasValidIceAttributes() const;
249 : void addLocalIceAttributes();
250 :
251 2081 : std::shared_ptr<dhtnet::IceTransport> getIceMedia() const
252 : {
253 2081 : std::lock_guard lk(transportMtx_);
254 4162 : return reinvIceMedia_ ? reinvIceMedia_ : iceMedia_;
255 2081 : };
256 :
257 : // Set ICE instance. Must be called only for sub-calls
258 : void setIceMedia(std::shared_ptr<dhtnet::IceTransport> ice, bool isReinvite = false);
259 :
260 : // Switch to re-invite ICE media if needed
261 : void switchToIceReinviteIfNeeded();
262 :
263 : /**
264 : * Setup ICE locally to answer to an ICE offer. The ICE session has
265 : * the controlled role (slave)
266 : */
267 : void setupIceResponse(bool isReinvite = false);
268 :
269 : void terminateSipSession(int status);
270 :
271 : /**
272 : * The invite session to be reused in case of transfer
273 : */
274 : struct InvSessionDeleter
275 : {
276 : void operator()(pjsip_inv_session*) const noexcept;
277 : };
278 :
279 : #ifdef ENABLE_VIDEO
280 : void setRotation(int streamIdx, int rotation);
281 : #endif
282 : // Get the list of current RTP sessions
283 : std::vector<std::shared_ptr<RtpSession>> getRtpSessionList(
284 : MediaType type = MediaType::MEDIA_ALL) const;
285 : static size_t getActiveMediaStreamCount(const std::vector<MediaAttribute>& mediaAttrList);
286 : void setActiveMediaStream(const std::string& accountUri,
287 : const std::string& deviceId,
288 : const std::string& streamId,
289 : const bool& state);
290 :
291 1 : void setPeerRegisteredName(const std::string& name)
292 : {
293 1 : peerRegisteredName_ = name;
294 1 : }
295 :
296 325 : void setPeerUri(const std::string& peerUri)
297 : {
298 325 : peerUri_ = peerUri;
299 325 : }
300 :
301 69 : std::string_view peerUri() const {
302 69 : return peerUri_;
303 : }
304 :
305 : // Create a new ICE media session. If we already have an instance,
306 : // it will be destroyed first.
307 : bool createIceMediaTransport(bool isReinvite);
308 :
309 : // Initialize the ICE session.
310 : // The initialization is performed asynchronously, i.e, the instance
311 : // may not be ready to use when this method returns.
312 : bool initIceMediaTransport(bool master,
313 : std::optional<dhtnet::IceTransportOptions> options = std::nullopt);
314 :
315 : std::vector<std::string> getLocalIceCandidates(unsigned compId) const;
316 :
317 : void setInviteSession(pjsip_inv_session* inviteSession = nullptr);
318 :
319 : std::unique_ptr<pjsip_inv_session, InvSessionDeleter> inviteSession_;
320 :
321 : inline std::weak_ptr<const SIPCall> weak() const
322 : {
323 : return std::weak_ptr<const SIPCall>(shared());
324 : }
325 2933 : inline std::weak_ptr<SIPCall> weak()
326 : {
327 2933 : return std::weak_ptr<SIPCall>(shared());
328 : }
329 : /**
330 : * Announce to the client that medias are successfully negotiated
331 : */
332 : void reportMediaNegotiationStatus();
333 :
334 : const std::vector<RtpStream>& getRtpStreams() const { return rtpStreams_; }
335 :
336 : private:
337 : void generateMediaPorts();
338 :
339 : void openPortsUPnP();
340 :
341 : bool isIceRunning() const;
342 :
343 : std::unique_ptr<dhtnet::IceSocket> newIceSocket(unsigned compId);
344 :
345 : void deinitRecorder();
346 :
347 : void rtpSetupSuccess();
348 :
349 : void setupVoiceCallback(const std::shared_ptr<RtpSession>& rtpSession);
350 :
351 : void sendMuteState(bool state);
352 : void sendVoiceActivity(std::string_view streamId, bool state);
353 :
354 : void resetTransport(std::shared_ptr<dhtnet::IceTransport>&& transport);
355 :
356 : /**
357 : * Send device orientation through SIP INFO
358 : * @param streamIdx The stream to rotate
359 : * @param rotation Device orientation (0/90/180/270) (counterclockwise)
360 : */
361 : void setVideoOrientation(int streamIdx, int rotation);
362 :
363 : mutable std::mutex transportMtx_ {};
364 :
365 : #ifdef ENABLE_PLUGIN
366 : /**
367 : * Call Streams and some typedefs
368 : */
369 : using AVMediaStream = Observable<std::shared_ptr<MediaFrame>>;
370 : using MediaStreamSubject = PublishMapSubject<std::shared_ptr<MediaFrame>, AVFrame*>;
371 :
372 : /**
373 : * @brief createCallAVStream
374 : * Creates a call AV stream like video input, video receive, audio input or audio receive
375 : * @param StreamData The type of the stream (audio/video, input/output,
376 : * @param streamSource
377 : * @param mediaStreamSubject
378 : */
379 : void createCallAVStream(const StreamData& StreamData,
380 : AVMediaStream& streamSource,
381 : const std::shared_ptr<MediaStreamSubject>& mediaStreamSubject);
382 : /**
383 : * @brief createCallAVStreams
384 : * Creates all Call AV Streams (2 if audio, 4 if audio video)
385 : */
386 : void createCallAVStreams();
387 :
388 : /**
389 : * @brief Detach all plugins from call streams;
390 : */
391 : void clearCallAVStreams();
392 :
393 : std::mutex avStreamsMtx_ {};
394 : std::map<std::string, std::shared_ptr<MediaStreamSubject>> callAVStreams;
395 : #endif // ENABLE_PLUGIN
396 :
397 : void setCallMediaLocal();
398 : void startIceMedia();
399 : void onIceNegoSucceed();
400 : void setupNegotiatedMedia();
401 : void startAllMedia();
402 : void stopAllMedia();
403 :
404 : /**
405 : * Transfer method used for both type of transfer
406 : */
407 : bool transferCommon(const pj_str_t* dst);
408 :
409 : bool internalOffHold(const std::function<void()>& SDPUpdateFunc);
410 :
411 : bool hold();
412 :
413 : bool unhold();
414 :
415 : // Update the attributes of a media stream
416 : void updateMediaStream(const MediaAttribute& newMediaAttr, size_t streamIdx);
417 : bool updateAllMediaStreams(const std::vector<MediaAttribute>& mediaAttrList, bool isRemote);
418 : // Check if a SIP re-invite must be sent to negotiate the new media
419 : bool isReinviteRequired(const std::vector<MediaAttribute>& mediaAttrList);
420 : // Check if a new ICE media session is needed when performing a re-invite
421 : bool isNewIceMediaRequired(const std::vector<MediaAttribute>& mediaAttrList);
422 : void requestReinvite(const std::vector<MediaAttribute>& mediaAttrList, bool needNewIce);
423 : int SIPSessionReinvite(const std::vector<MediaAttribute>& mediaAttrList, bool needNewIce);
424 : int SIPSessionReinvite();
425 : // Add a media stream to the call.
426 : void addMediaStream(const MediaAttribute& mediaAttr);
427 : // Init media streams
428 : size_t initMediaStreams(const std::vector<MediaAttribute>& mediaAttrList);
429 : // Create a new stream from SDP description.
430 : void createRtpSession(RtpStream& rtpStream);
431 : // Configure the RTP session from SDP description.
432 : void configureRtpSession(const std::shared_ptr<RtpSession>& rtpSession,
433 : const std::shared_ptr<MediaAttribute>& mediaAttr,
434 : const MediaDescription& localMedia,
435 : const MediaDescription& remoteMedia);
436 : // Find the stream index with the matching label
437 : int findRtpStreamIndex(const std::string& label) const;
438 :
439 : std::vector<IceCandidate> getAllRemoteCandidates(dhtnet::IceTransport& transport) const;
440 :
441 : inline std::shared_ptr<const SIPCall> shared() const
442 : {
443 : return std::static_pointer_cast<const SIPCall>(shared_from_this());
444 : }
445 2933 : inline std::shared_ptr<SIPCall> shared()
446 : {
447 2933 : return std::static_pointer_cast<SIPCall>(shared_from_this());
448 : }
449 :
450 : // Peer's User-Agent.
451 : std::string peerUserAgent_ {};
452 : // Flag to indicate if the peer's Daemon version supports multi-stream.
453 : bool peerSupportMultiStream_ {false};
454 : // Flag to indicate if the peer's Daemon version supports multi-stream.
455 : bool peerSupportMultiAudioStream_ {false};
456 : // Flag to indicate if the peer's Daemon version can negotiate more than 2 ICE medias
457 : bool peerSupportMultiIce_ {false};
458 :
459 : // Flag to indicate if the peer's Daemon version supports re-invite
460 : // without ICE renegotiation.
461 : bool peerSupportReuseIceInReinv_ {false};
462 :
463 : // Peer's allowed methods.
464 : std::vector<std::string> peerAllowedMethods_;
465 :
466 : // Vector holding the current RTP sessions.
467 : std::vector<RtpStream> rtpStreams_;
468 :
469 : /**
470 : * Hold the transport used for SIP communication.
471 : * Will be different from the account registration transport for
472 : * non-IP2IP calls.
473 : */
474 : std::shared_ptr<SipTransport> sipTransport_ {};
475 :
476 : /**
477 : * The SDP session
478 : */
479 : std::unique_ptr<Sdp> sdp_ {};
480 : bool peerHolding_ {false};
481 :
482 : bool isWaitingForIceAndMedia_ {false};
483 : enum class Request { HoldingOn, HoldingOff, SwitchInput, NoRequest };
484 : Request remainingRequest_ {Request::NoRequest};
485 :
486 : std::string peerRegisteredName_ {};
487 :
488 : std::string contactHeader_ {};
489 :
490 : std::shared_ptr<dhtnet::upnp::Controller> upnp_;
491 :
492 : /** Local audio port, as seen by me. */
493 : unsigned int localAudioPort_ {0};
494 : /** Local video port, as seen by me. */
495 : unsigned int localVideoPort_ {0};
496 :
497 : bool mediaRestartRequired_ {true};
498 : bool enableIce_ {true};
499 : bool srtpEnabled_ {false};
500 : bool rtcpMuxEnabled_ {false};
501 :
502 : // ICE media transport
503 : std::shared_ptr<dhtnet::IceTransport> iceMedia_;
504 : // Re-invite (temporary) ICE media transport.
505 : std::shared_ptr<dhtnet::IceTransport> reinvIceMedia_;
506 :
507 : std::string peerUri_ {};
508 :
509 : bool readyToRecord_ {false};
510 : bool pendingRecord_ {false};
511 :
512 : time_point lastKeyFrameReq_ {time_point::min()};
513 :
514 : OnReadyCb holdCb_ {};
515 : OnReadyCb offHoldCb_ {};
516 :
517 : std::atomic_bool waitForIceInit_ {false};
518 :
519 : void detachAudioFromConference();
520 :
521 : std::mutex setupSuccessMutex_;
522 : #ifdef ENABLE_VIDEO
523 : int rotation_ {0};
524 : #endif
525 :
526 : std::string mediaPlayerId_ {};
527 : };
528 :
529 : // Helpers
530 :
531 : /**
532 : * Obtain a shared smart pointer of instance
533 : */
534 : inline std::shared_ptr<SIPCall>
535 : getPtr(SIPCall& call)
536 : {
537 : return std::static_pointer_cast<SIPCall>(call.shared_from_this());
538 : }
539 :
540 : } // namespace jami
|