Ring Daemon 16.0.0
Loading...
Searching...
No Matches
conference.h
Go to the documentation of this file.
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 <chrono>
24#include <set>
25#include <string>
26#include <memory>
27#include <vector>
28#include <string_view>
29#include <map>
30#include <functional>
31
32#include "conference_protocol.h"
35#include "media/recordable.h"
36
37#ifdef ENABLE_PLUGIN
38#include "plugin/streamdata.h"
39#endif
40
41#ifdef ENABLE_VIDEO
43#endif
44
45#include <json/json.h>
46
47namespace jami {
48
49class Call;
50class Account;
51
52#ifdef ENABLE_VIDEO
53namespace video {
54class VideoMixer;
55}
56#endif
57
58// info for a stream
60{
61 std::string uri;
62 std::string device;
63 std::string sinkId; // stream ID
64 bool active {false};
65 int x {0};
66 int y {0};
67 int w {0};
68 int h {0};
69 bool videoMuted {false};
70 bool audioLocalMuted {false};
71 bool audioModeratorMuted {false};
72 bool isModerator {false};
73 bool handRaised {false};
74 bool voiceActivity {false};
75 bool recording {false};
76
77 void fromJson(const Json::Value& v)
78 {
79 uri = v["uri"].asString();
80 device = v["device"].asString();
81 sinkId = v["sinkId"].asString();
82 active = v["active"].asBool();
83 x = v["x"].asInt();
84 y = v["y"].asInt();
85 w = v["w"].asInt();
86 h = v["h"].asInt();
87 videoMuted = v["videoMuted"].asBool();
88 audioLocalMuted = v["audioLocalMuted"].asBool();
89 audioModeratorMuted = v["audioModeratorMuted"].asBool();
90 isModerator = v["isModerator"].asBool();
91 handRaised = v["handRaised"].asBool();
92 voiceActivity = v["voiceActivity"].asBool();
93 recording = v["recording"].asBool();
94 }
95
96 Json::Value toJson() const
97 {
98 Json::Value val;
99 val["uri"] = uri;
100 val["device"] = device;
101 val["sinkId"] = sinkId;
102 val["active"] = active;
103 val["x"] = x;
104 val["y"] = y;
105 val["w"] = w;
106 val["h"] = h;
107 val["videoMuted"] = videoMuted;
108 val["audioLocalMuted"] = audioLocalMuted;
109 val["audioModeratorMuted"] = audioModeratorMuted;
110 val["isModerator"] = isModerator;
111 val["handRaised"] = handRaised;
112 val["voiceActivity"] = voiceActivity;
113 val["recording"] = recording;
114 return val;
115 }
116
117 std::map<std::string, std::string> toMap() const
118 {
119 return {{"uri", uri},
120 {"device", device},
121 {"sinkId", sinkId},
122 {"active", active ? "true" : "false"},
123 {"x", std::to_string(x)},
124 {"y", std::to_string(y)},
125 {"w", std::to_string(w)},
126 {"h", std::to_string(h)},
127 {"videoMuted", videoMuted ? "true" : "false"},
128 {"audioLocalMuted", audioLocalMuted ? "true" : "false"},
129 {"audioModeratorMuted", audioModeratorMuted ? "true" : "false"},
130 {"isModerator", isModerator ? "true" : "false"},
131 {"handRaised", handRaised ? "true" : "false"},
132 {"voiceActivity", voiceActivity ? "true" : "false"},
133 {"recording", recording ? "true" : "false"}};
134 }
135
136 friend bool operator==(const ParticipantInfo& p1, const ParticipantInfo& p2)
137 {
138 return p1.uri == p2.uri and p1.device == p2.device and p1.sinkId == p2.sinkId
139 and p1.active == p2.active and p1.x == p2.x and p1.y == p2.y and p1.w == p2.w
140 and p1.h == p2.h and p1.videoMuted == p2.videoMuted
141 and p1.audioLocalMuted == p2.audioLocalMuted
142 and p1.audioModeratorMuted == p2.audioModeratorMuted
143 and p1.isModerator == p2.isModerator and p1.handRaised == p2.handRaised
144 and p1.voiceActivity == p2.voiceActivity and p1.recording == p2.recording;
145 }
146
147 friend bool operator!=(const ParticipantInfo& p1, const ParticipantInfo& p2)
148 {
149 return !(p1 == p2);
150 }
151};
152
153struct ConfInfo : public std::vector<ParticipantInfo>
154{
155 int h {0};
156 int w {0};
157 int v {1}; // Supported conference protocol version
158 int layout {0};
159
160 friend bool operator==(const ConfInfo& c1, const ConfInfo& c2)
161 {
162 if (c1.h != c2.h or c1.w != c2.w)
163 return false;
164 if (c1.size() != c2.size())
165 return false;
166
167 for (auto& p1 : c1) {
168 auto it = std::find_if(c2.begin(), c2.end(), [&p1](const ParticipantInfo& p2) {
169 return p1 == p2;
170 });
171 if (it != c2.end())
172 continue;
173 else
174 return false;
175 }
176 return true;
177 }
178
179 friend bool operator!=(const ConfInfo& c1, const ConfInfo& c2) { return !(c1 == c2); }
180
181 std::vector<std::map<std::string, std::string>> toVectorMapStringString() const;
182 std::string toString() const;
183};
184
185using CallIdSet = std::set<std::string>;
186using clock = std::chrono::steady_clock;
187
188class Conference : public Recordable, public std::enable_shared_from_this<Conference>
189{
190public:
192
196 explicit Conference(const std::shared_ptr<Account>&,
197 const std::string& confId = "");
198
202 ~Conference();
203
207 const std::string& getConfId() const { return id_; }
208
209 std::shared_ptr<Account> getAccount() const { return account_.lock(); }
210
211 std::string getAccountId() const;
212
216 State getState() const;
217
221 void setState(State state);
222
226 void onShutdown(std::function<void(int)> cb) { shutdownCb_ = std::move(cb); }
227
231 static constexpr const char* getStateStr(State state)
232 {
233 switch (state) {
235 return "ACTIVE_ATTACHED";
237 return "ACTIVE_DETACHED";
238 case State::HOLD:
239 return "HOLD";
240 default:
241 return "";
242 }
243 }
244
245 const char* getStateStr() const { return getStateStr(confState_); }
246
250 void setLocalHostMuteState(MediaType type, bool muted);
251
255 bool isMediaSourceMuted(MediaType type) const;
256
264 bool requestMediaChange(const std::vector<libjami::MediaMap>& mediaList);
265
272 void handleMediaChangeRequest(const std::shared_ptr<Call>& call,
273 const std::vector<libjami::MediaMap>& remoteMediaList);
274
278 void addSubCall(const std::string& callId);
279
283 void removeSubCall(const std::string& callId);
284
288 void attachHost(const std::vector<libjami::MediaMap>& mediaList = {});
289
293 void detachHost();
294
298 CallIdSet getSubCalls() const;
299
303 bool toggleRecording() override;
304
305 void switchInput(const std::string& input);
306 void setActiveParticipant(const std::string& participant_id);
307 void setActiveStream(const std::string& streamId, bool state);
308 void setLayout(int layout);
309
310 void onConfOrder(const std::string& callId, const std::string& order);
311
312 bool isVideoEnabled() const;
313
314#ifdef ENABLE_VIDEO
315 void createSinks(const ConfInfo& infos);
316 std::shared_ptr<video::VideoMixer> getVideoMixer();
317 std::string getVideoInput() const;
318#endif
319
320 std::vector<std::map<std::string, std::string>> getConferenceInfos() const
321 {
322 std::lock_guard lk(confInfoMutex_);
323 return confInfo_.toVectorMapStringString();
324 }
325
327 void setModerator(const std::string& uri, const bool& state);
328 void hangupParticipant(const std::string& accountUri, const std::string& deviceId = "");
329 void setHandRaised(const std::string& uri, const bool& state);
330 void setVoiceActivity(const std::string& streamId, const bool& newState);
331
332 void muteParticipant(const std::string& uri, const bool& state);
333 void muteLocalHost(bool is_muted, const std::string& mediaType);
334 bool isRemoteParticipant(const std::string& uri);
335 void mergeConfInfo(ConfInfo& newInfo, const std::string& peerURI);
336
346 void muteStream(const std::string& accountUri,
347 const std::string& deviceId,
348 const std::string& streamId,
349 const bool& state);
350 void updateMuted();
351 void updateRecording();
352
353 void updateVoiceActivity();
354
355 std::shared_ptr<Call> getCallFromPeerID(std::string_view peerId);
356
361
366 std::vector<libjami::MediaMap> currentMediaList() const;
367
368 // Update layout if recording changes
369 void stopRecording() override;
370 bool startRecording(const std::string& path) override;
371
375 std::chrono::milliseconds getDuration() const
376 {
377 return duration_start_ == clock::time_point::min()
378 ? std::chrono::milliseconds::zero()
379 : std::chrono::duration_cast<std::chrono::milliseconds>(clock::now()
380 - duration_start_);
381 }
382
383private:
384 std::weak_ptr<Conference> weak()
385 {
386 return std::static_pointer_cast<Conference>(shared_from_this());
387 }
388
389 static std::shared_ptr<Call> getCall(const std::string& callId);
390 bool isModerator(std::string_view uri) const;
391 bool isHandRaised(std::string_view uri) const;
392 bool isVoiceActive(std::string_view uri) const;
393 void updateModerators();
394 void updateHandsRaised();
395 void muteHost(bool state);
396 void muteCall(const std::string& callId, bool state);
397
398 void foreachCall(const std::function<void(const std::shared_ptr<Call>& call)>& cb);
399
400 std::string id_;
401 std::weak_ptr<Account> account_;
402 State confState_ {State::ACTIVE_DETACHED};
403 mutable std::mutex subcallsMtx_ {};
404 CallIdSet subCalls_;
405 std::string mediaPlayerId_ {};
406
407 mutable std::mutex confInfoMutex_ {};
408 ConfInfo confInfo_ {};
409
410 void sendConferenceInfos();
411 std::shared_ptr<RingBuffer> ghostRingBuffer_;
412
413#ifdef ENABLE_VIDEO
414 bool videoEnabled_;
415 std::shared_ptr<video::VideoMixer> videoMixer_;
416 std::map<std::string, std::shared_ptr<video::SinkClient>> confSinksMap_ {};
417#endif
418
419 std::shared_ptr<jami::AudioInput> audioMixer_;
420 std::set<std::string, std::less<>> moderators_ {};
421 std::set<std::string, std::less<>> participantsMuted_ {};
422 std::set<std::string, std::less<>> handsRaised_;
423
424 // stream IDs
425 std::set<std::string, std::less<>> streamsVoiceActive {};
426
427 void initRecorder(std::shared_ptr<MediaRecorder>& rec);
428 void deinitRecorder(std::shared_ptr<MediaRecorder>& rec);
429
430 bool isMuted(std::string_view uri) const;
431
432 ConfInfo getConfInfoHostUri(std::string_view localHostURI, std::string_view destURI);
433 bool isHost(std::string_view uri) const;
434 bool isHostDevice(std::string_view deviceId) const;
435
441 std::vector<MediaAttribute> hostSources_;
442 // Because host doesn't have a call, we need to store the audio inputs
443 std::map<std::string, std::shared_ptr<jami::AudioInput>> hostAudioInputs_;
444
445 bool localModAdded_ {false};
446
447 std::map<std::string, ConfInfo> remoteHosts_;
448#ifdef ENABLE_VIDEO
449 void resizeRemoteParticipants(ConfInfo& confInfo, std::string_view peerURI);
450#endif
451 std::string_view findHostforRemoteParticipant(std::string_view uri,
452 std::string_view deviceId = "");
453
454 std::shared_ptr<Call> getCallWith(const std::string& accountUri, const std::string& deviceId);
455
456 std::mutex sinksMtx_ {};
457
458#ifdef ENABLE_PLUGIN
464
465#ifdef ENABLE_VIDEO
469 std::function<AVFrame*(const std::shared_ptr<jami::MediaFrame>&)> pluginVideoMap_ =
470 [](const std::shared_ptr<jami::MediaFrame>& m) -> AVFrame* {
471 return std::static_pointer_cast<VideoFrame>(m)->pointer();
472 };
473#endif // ENABLE_VIDEO
474
484 const std::shared_ptr<MediaStreamSubject>& mediaStreamSubject,
485 bool force = false);
490 void createConfAVStreams();
491
492 std::mutex avStreamsMtx_ {};
493 std::map<std::string, std::shared_ptr<MediaStreamSubject>> confAVStreams;
494#endif // ENABLE_PLUGIN
495
496 ConfProtocolParser parser_;
497 std::string getRemoteId(const std::shared_ptr<jami::Call>& call) const;
498
499 std::function<void(int)> shutdownCb_;
500 clock::time_point duration_start_;
501
505 void initSourcesForHost();
506
513 void takeOverMediaSourceControl(const std::string& callId);
514
518 void bindHostAudio();
519
523 void unbindHostAudio();
524
528 void bindSubCallAudio(const std::string& callId);
529
533 void unbindSubCallAudio(const std::string& callId);
534};
535
536} // namespace jami
const char * getStateStr() const
Definition conference.h:245
void attachHost(const std::vector< libjami::MediaMap > &mediaList={})
Attach host.
void setVoiceActivity(const std::string &streamId, const bool &newState)
std::string getAccountId() const
bool isVideoEnabled() const
void hangupParticipant(const std::string &accountUri, const std::string &deviceId="")
void muteLocalHost(bool is_muted, const std::string &mediaType)
std::chrono::milliseconds getDuration() const
Definition conference.h:375
bool startRecording(const std::string &path) override
Start recording.
const std::string & getConfId() const
Return the conference id.
Definition conference.h:207
void removeSubCall(const std::string &callId)
Remove a subcall from the conference.
std::vector< libjami::MediaMap > currentMediaList() const
Retrieve current medias list.
std::vector< std::map< std::string, std::string > > getConferenceInfos() const
Definition conference.h:320
void detachHost()
Detach local audio/video from the conference.
~Conference()
Destructor for this class, decrement static counter.
void mergeConfInfo(ConfInfo &newInfo, const std::string &peerURI)
void setActiveParticipant(const std::string &participant_id)
void handleMediaChangeRequest(const std::shared_ptr< Call > &call, const std::vector< libjami::MediaMap > &remoteMediaList)
Process incoming media change request.
void setLocalHostMuteState(MediaType type, bool muted)
Set the mute state of the local host.
void setState(State state)
Set conference state.
bool requestMediaChange(const std::vector< libjami::MediaMap > &mediaList)
Process a media change request.
void onConfOrder(const std::string &callId, const std::string &order)
void addSubCall(const std::string &callId)
Add a new subcall to the conference.
std::shared_ptr< Account > getAccount() const
Definition conference.h:209
void onShutdown(std::function< void(int)> cb)
Set a callback that will be called when the conference will be destroyed.
Definition conference.h:226
std::shared_ptr< Call > getCallFromPeerID(std::string_view peerId)
void setActiveStream(const std::string &streamId, bool state)
void setModerator(const std::string &uri, const bool &state)
static constexpr const char * getStateStr(State state)
Return a string description of the conference state.
Definition conference.h:231
void stopRecording() override
Stop recording.
bool toggleRecording() override
Start/stop recording toggle.
CallIdSet getSubCalls() const
Get the participant list for this conference.
void setHandRaised(const std::string &uri, const bool &state)
void muteParticipant(const std::string &uri, const bool &state)
State getState() const
Return the current conference state.
void muteStream(const std::string &accountUri, const std::string &deviceId, const std::string &streamId, const bool &state)
The client shows one tile per stream (video/audio related to a media)
void updateConferenceInfo(ConfInfo confInfo)
void switchInput(const std::string &input)
bool isRemoteParticipant(const std::string &uri)
void setLayout(int layout)
void reportMediaNegotiationStatus()
Announce to the client that medias are successfully negotiated.
bool isMediaSourceMuted(MediaType type) const
Get the mute state of the local host.
std::set< std::string > CallIdSet
Definition conference.h:185
void emitSignal(Args... args)
Definition ring_signal.h:64
std::chrono::steady_clock clock
Definition conference.h:186
Contains information about an AV subject.
Definition streamdata.h:29
std::string toString() const
std::vector< std::map< std::string, std::string > > toVectorMapStringString() const
friend bool operator!=(const ConfInfo &c1, const ConfInfo &c2)
Definition conference.h:179
friend bool operator==(const ConfInfo &c1, const ConfInfo &c2)
Definition conference.h:160
Json::Value toJson() const
Definition conference.h:96
friend bool operator==(const ParticipantInfo &p1, const ParticipantInfo &p2)
Definition conference.h:136
std::map< std::string, std::string > toMap() const
Definition conference.h:117
friend bool operator!=(const ParticipantInfo &p1, const ParticipantInfo &p2)
Definition conference.h:147
void fromJson(const Json::Value &v)
Definition conference.h:77