48#include <opendht/thread_pool.h>
50using namespace std::literals;
62 JAMI_LOG(
"Create new conference {}", id_);
63 duration_start_ = clock::now();
66 videoMixer_ = std::make_shared<video::VideoMixer>(id_);
67 videoMixer_->setOnSourcesUpdated([
this](std::vector<video::SourceInfo>&& infos) {
69 auto shared = w.lock();
72 auto acc = std::dynamic_pointer_cast<JamiAccount>(shared->account_.lock());
77 std::lock_guard lock(shared->confInfoMutex_);
78 newInfo.w = shared->confInfo_.w;
79 newInfo.h = shared->confInfo_.h;
80 newInfo.layout = shared->confInfo_.layout;
84 for (
const auto& info : infos) {
87 std::string deviceId {};
89 if (!info.callId.empty()) {
90 std::string callId = info.callId;
91 if (
auto call = std::dynamic_pointer_cast<SIPCall>(getCall(callId))) {
92 uri = call->getPeerNumber();
94 isPeerRecording = call->isPeerRecording();
95 if (
auto* transport = call->getTransport())
96 deviceId = transport->deviceId();
99 auto isModerator = shared->isModerator(peerId);
100 auto isHandRaised = shared->isHandRaised(deviceId);
102 auto isVoiceActive = shared->isVoiceActive(info.streamId);
104 active =
videoMixer->verifyActive(info.streamId);
107 std::move(info.streamId),
123 auto streamInfo = shared->videoMixer_->streamInfo(info.source);
124 std::string streamId = streamInfo.streamId;
125 if (!streamId.empty()) {
134 if (
auto call = std::dynamic_pointer_cast<SIPCall>(
135 getCall(streamInfo.callId))) {
136 uri = call->getPeerNumber();
138 isPeerRecording = call->isPeerRecording();
139 if (
auto* transport = call->getTransport())
140 deviceId = transport->deviceId();
148 auto isModerator = shared->isModerator(peerId);
152 deviceId = acc->currentDeviceId();
154 isPeerRecording = shared->isRecording();
156 auto isHandRaised = shared->isHandRaised(deviceId);
157 auto isVoiceActive = shared->isVoiceActive(streamId);
187 shared->updateConferenceInfo(std::move(
newInfo));
194#if defined(__APPLE__) && TARGET_OS_MAC
200 JAMI_ERR(
"Conference resolution is invalid");
211 [&](
const auto& streamId,
bool state) {
setActiveStream(streamId, state); });
213 [&](
const auto&
accountUri,
const auto& deviceId,
const auto& streamId,
bool state) {
226 if (
auto* transport = call->getTransport())
231 [&](
const auto& streamId,
bool state) {
setVoiceActivity(streamId, state); });
237 JAMI_INFO(
"Destroying conference %s", id_.c_str());
243 foreachCall([&](
auto call) {
244 call->exitConference();
246 call->resetConfInfo();
255 if (
not call->isRecording()) {
256 JAMI_DEBUG(
"Conference was recorded, start recording for conf {:s}",
258 call->toggleRecording();
262 if (call->isPeerRecording())
263 call->peerRecording(
true);
266 auto&
sink = videoMixer_->getSink();
268 sink->detach(
it->second.get());
278 .getJamiPluginManager()
279 .getCallServicesManager()
281 Manager::instance().getJamiPluginManager().getCallServicesManager().clearAVSubject(
302 JAMI_DEBUG(
"[conf {:s}] Set state to [{:s}] (was [{:s}])",
311Conference::initSourcesForHost()
313 hostSources_.clear();
321 JAMI_DEBUG(
"[conf {:s}] Setting local host audio source to [{:s}]", id_,
audioAttr.toString());
337 JAMI_DEBUG(
"[conf {:s}] Setting local host video source to [{:s}]",
356std::vector<std::map<std::string, std::string>>
364Conference::createConfAVStreams()
368 auto audioMap = [](
const std::shared_ptr<jami::MediaFrame>&
m) ->
AVFrame* {
369 return std::static_pointer_cast<AudioFrame>(
m)->pointer();
420 .getJamiPluginManager()
421 .getCallServicesManager()
429 for (
auto& source : hostSources_)
430 if (source.type_ == type)
431 source.muted_ =
muted;
448 for (
const auto& source : hostSources_) {
449 if (source.muted_ && source.type_ == type)
452 JAMI_WARN(
"The host source for %s is not set. The mute state is meaningless",
453 source.mediaTypeToString(source.type_));
462Conference::takeOverMediaSourceControl(
const std::string& callId)
464 auto call = getCall(callId);
466 JAMI_ERR(
"No call matches participant %s", callId.c_str());
470 auto account = call->getAccount().lock();
472 JAMI_ERR(
"No account detected for call %s", callId.c_str());
476 auto mediaList = call->getMediaAttributeList();
491 JAMI_DEBUG(
"[Call: {:s}] Does not have an active [{:s}] media source",
501 if (subCalls_.size() == 1) {
516 JAMI_WARN(
"Take over [AUDIO] control from call %s - current local source state [%s]",
518 muted ?
"muted" :
"un-muted");
522 JAMI_WARN(
"Take over [VIDEO] control from call %s - current local source state [%s]",
524 muted ?
"muted" :
"un-muted");
534 JAMI_ERROR(
"[conf {}] Request media change can be performed only in attached mode",
545 if (!
media.enabled_ ||
media.sourceUri_.empty())
552 if (
pos == std::string::npos)
561 mediaPlayerId_ =
media.sourceUri_;
578 auto oldIdx = std::find_if(hostSources_.begin(), hostSources_.end(), [&](
auto oldAttr) {
579 return oldAttr.label_ == mediaAttr.label_;
588 srcUri =
vm->videoDeviceMonitor.getMRLForDefaultDevice();
600 if (
oldIdx != hostSources_.end()) {
645 auto callId = call->getCallId();
646 videoMixer_->removeAudioOnlySource(
683 JAMI_DEBUG(
"Adding call {:s} to conference {:s}", callId, id_);
689 std::lock_guard
lk(subcallsMtx_);
690 if (!subCalls_.insert(callId).second)
694 if (
auto call = std::dynamic_pointer_cast<SIPCall>(getCall(callId))) {
696 if (call->isPeerMuted())
697 participantsMuted_.emplace(call->getCallId());
702 takeOverMediaSourceControl(callId);
703 auto w = call->getAccount();
707 for (
const auto&
mod :
account->getDefaultModerators()) {
708 moderators_.emplace(
mod);
712 if (
account->isLocalModeratorsEnabled() &&
not localModAdded_) {
715 moderators_.emplace(
account->getUsername());
717 localModAdded_ =
true;
721 if (
account->isAllModerators())
722 moderators_.emplace(getRemoteId(call));
727 auto mediaList = call->getMediaAttributeList();
728 if (call->peerUri().find(
"swarm:") != 0) {
730 videoMixer_->addAudioOnlySource(call->getCallId(),
737 if (call->isRecording()) {
738 JAMI_DEBUG(
"Stop recording for call {:s}", call->getCallId());
739 call->toggleRecording();
741 JAMI_DEBUG(
"One participant was recording, start recording for conference {:s}",
746 bindSubCallAudio(callId);
749 JAMI_ERROR(
"no call associate to participant {}", callId.c_str());
758 JAMI_DEBUG(
"Remove call {:s} in conference {:s}", callId, id_);
760 std::lock_guard
lk(subcallsMtx_);
761 if (!subCalls_.erase(callId))
764 if (
auto call = std::dynamic_pointer_cast<SIPCall>(getCall(callId))) {
765 const auto& peerId = getRemoteId(call);
766 participantsMuted_.erase(call->getCallId());
767 if (
auto* transport = call->getTransport())
768 handsRaised_.erase(std::string(transport->deviceId()));
771 for (
auto const&
rtpSession : call->getRtpSessionList()) {
773 videoMixer_->removeAudioOnlySource(callId,
rtpSession->streamId());
774 if (videoMixer_->verifyActive(
rtpSession->streamId()))
775 videoMixer_->resetActiveStream();
780 unbindSubCallAudio(callId);
781 call->exitConference();
782 if (call->isPeerRecording())
783 call->peerRecording(
false);
799 videoMixer_->setActiveStream(
807 JAMI_WARN(
"Change remote layout is not supported");
811 videoMixer_->resetActiveStream();
822 videoMixer_->setActiveStream(streamId);
824 videoMixer_->resetActiveStream();
833 JAMI_ERR(
"Unknown layout %u", layout);
839 std::lock_guard
lk(confInfoMutex_);
840 confInfo_.
layout = layout;
842 videoMixer_->setVideoLayout(
static_cast<video::Layout>(layout));
846std::vector<std::map<std::string, std::string>>
849 std::vector<std::map<std::string, std::string>> infos;
850 infos.reserve(size());
851 for (
const auto& info : *
this)
852 infos.emplace_back(info.toMap());
859 Json::Value
val = {};
860 for (
const auto& info : *
this) {
861 val[
"p"].append(info.toJson());
871Conference::sendConferenceInfos()
874 foreachCall([&](
auto call) {
877 auto w = call->getAccount();
882 dht::ThreadPool::io().run(
885 call->getPeerNumber())] {
886 call->sendConfInfo(confInfo.toString());
890 auto confInfo = getConfInfoHostUri(
"",
"");
899 .toVectorMapStringString());
904Conference::createSinks(
const ConfInfo& infos)
906 std::lock_guard
lk(sinksMtx_);
909 auto&
sink = videoMixer_->getSink();
912 {std::static_pointer_cast<video::VideoFrameActiveWriter>(
921 JAMI_LOG(
"Attach local participant to conference {}", id_);
926 initSourcesForHost();
931 for (
const auto& source : hostSources_) {
947 "Invalid conference state in attach participant: current \"{}\" - expected \"{}\"",
956 JAMI_LOG(
"Detach local participant from conference {}", id_);
963 videoMixer_->stopInputs();
967 "Invalid conference state in detach participant: current \"{}\" - expected \"{}\"",
974 initSourcesForHost();
980 std::lock_guard
lk(subcallsMtx_);
994 foreachCall([&](
auto call) { call->updateRecState(
newState); });
1005 return account->getAccountID();
1013 JAMI_DEBUG(
"[Conf:{:s}] Setting video input to {:s}", id_, input);
1018 for (
auto& source : hostSources_) {
1022 source.sourceUri_ = input;
1034 if (
auto mixer = videoMixer_) {
1035 mixer->switchInputs({input});
1055 if (
auto shared = account_.lock())
1056 return shared->isVideoEnabled();
1061std::shared_ptr<video::VideoMixer>
1062Conference::getVideoMixer()
1068Conference::getVideoInput()
const
1070 for (
const auto& source : hostSources_) {
1072 return source.sourceUri_;
1079Conference::initRecorder(std::shared_ptr<MediaRecorder>&
rec)
1084 if (
auto ob =
rec->addStream(videoMixer_->getStream(
"v:mixer"))) {
1085 videoMixer_->attach(
ob);
1100 if (
auto ob =
rec->addStream(audioMixer_->getInfo(
"a:mixer"))) {
1101 audioMixer_->attach(
ob);
1106Conference::deinitRecorder(std::shared_ptr<MediaRecorder>&
rec)
1111 if (
auto ob =
rec->getStream(
"v:mixer")) {
1112 videoMixer_->detach(
ob);
1118 if (
auto ob =
rec->getStream(
"a:mixer"))
1119 audioMixer_->detach(
ob);
1120 audioMixer_.reset();
1122 ghostRingBuffer_.reset();
1129 if (
auto call = getCall(callId)) {
1130 const auto& peerId = getRemoteId(call);
1133 JAMI_WARNING(
"Unable to parse conference order from {}", peerId);
1142std::shared_ptr<Call>
1143Conference::getCall(
const std::string& callId)
1149Conference::isModerator(std::string_view uri)
const
1151 return moderators_.find(uri) != moderators_.end()
or isHost(uri);
1155Conference::isHandRaised(std::string_view deviceId)
const
1157 return isHostDevice(deviceId) ? handsRaised_.find(
"host"sv) != handsRaised_.end()
1158 : handsRaised_.find(deviceId) != handsRaised_.end();
1164 if (isHostDevice(deviceId)) {
1168 handsRaised_.emplace(
"host"sv);
1169 updateHandsRaised();
1172 handsRaised_.erase(
"host");
1173 updateHandsRaised();
1177 if (
auto call = std::dynamic_pointer_cast<SIPCall>(getCall(p))) {
1180 if (
auto* transport = call->getTransport())
1185 handsRaised_.emplace(deviceId);
1186 updateHandsRaised();
1188 JAMI_DEBUG(
"Remove {:s} raised hand", deviceId);
1189 handsRaised_.erase(deviceId);
1190 updateHandsRaised();
1196 JAMI_WARN(
"Fail to raise %s hand (participant not found)", deviceId.c_str());
1201Conference::isVoiceActive(std::string_view streamId)
const
1203 return streamsVoiceActive.find(streamId) != streamsVoiceActive.end();
1219 JAMI_ERR(
"participant not found with streamId: %s", streamId.c_str());
1232 streamsVoiceActive.emplace(streamId);
1239 streamsVoiceActive.erase(streamId);
1249 if (
auto call = getCall(p)) {
1269Conference::updateModerators()
1271 std::lock_guard
lk(confInfoMutex_);
1272 for (
auto& info : confInfo_) {
1275 sendConferenceInfos();
1279Conference::updateHandsRaised()
1281 std::lock_guard
lk(confInfoMutex_);
1282 for (
auto& info : confInfo_)
1283 info.handRaised = isHandRaised(info.device);
1284 sendConferenceInfos();
1290 std::lock_guard
lk(confInfoMutex_);
1310 sendConferenceInfos();
1314Conference::foreachCall(
const std::function<
void(
const std::shared_ptr<Call>& call)>&
cb)
1317 if (
auto call = getCall(p))
1322Conference::isMuted(std::string_view callId)
const
1324 return participantsMuted_.find(callId) != participantsMuted_.end();
1329 const std::string& deviceId,
1333 if (
auto acc = std::dynamic_pointer_cast<JamiAccount>(account_.lock())) {
1334 if (
accountUri == acc->getUsername() && deviceId == acc->currentDeviceId()) {
1336 }
else if (
auto call = getCallWith(
accountUri, deviceId)) {
1337 muteCall(call->getCallId(), state);
1345Conference::muteHost(
bool state)
1349 participantsMuted_.emplace(
"host"sv);
1355 participantsMuted_.erase(
"host");
1365Conference::muteCall(
const std::string& callId,
bool state)
1370 participantsMuted_.emplace(callId);
1371 unbindSubCallAudio(callId);
1374 JAMI_DEBUG(
"Unmute participant {:s}", callId);
1375 participantsMuted_.erase(callId);
1376 bindSubCallAudio(callId);
1392 auto w = call->getAccount();
1399 call->sendConfOrder(
root);
1408 muteCall(call->getCallId(), state);
1414 std::lock_guard
lk(confInfoMutex_);
1415 for (
auto& info : confInfo_) {
1416 if (info.uri.empty()) {
1420 info.recording = call->isPeerRecording();
1423 sendConferenceInfos();
1429 std::lock_guard
lk(confInfoMutex_);
1430 for (
auto& info : confInfo_) {
1431 if (info.uri.empty()) {
1432 info.audioModeratorMuted = isMuted(
"host"sv);
1436 info.audioModeratorMuted = isMuted(call->getCallId());
1437 info.audioLocalMuted = call->isPeerMuted();
1440 sendConferenceInfos();
1449 bool isRemoteHost = remoteHosts_.find(
it->uri) != remoteHosts_.end();
1481Conference::isHost(std::string_view uri)
const
1489 if (
auto call = getCall(p)) {
1490 if (
auto account = call->getAccount().lock()) {
1491 if (
account->getUsername() == uri)
1500Conference::isHostDevice(std::string_view deviceId)
const
1502 if (
auto acc = std::dynamic_pointer_cast<JamiAccount>(account_.lock()))
1503 return deviceId == acc->currentDeviceId();
1510 std::lock_guard
lk(confInfoMutex_);
1512 sendConferenceInfos();
1518 if (
auto acc = std::dynamic_pointer_cast<JamiAccount>(account_.lock())) {
1519 if (deviceId.empty()) {
1526 if (
accountUri == acc->getUsername() && deviceId == acc->currentDeviceId()) {
1529 }
else if (
auto call = getCallWith(
accountUri, deviceId)) {
1552 JAMI_DEBUG(
"Local audio source already in [{:s}] state",
1559 JAMI_DBG(
"Muting local audio source");
1562 JAMI_DBG(
"Un-muting local audio source");
1572 JAMI_ERROR(
"Unable to stop camera, the camera is disabled!");
1577 JAMI_DEBUG(
"Local camera source already in [{:s}] state",
1583 if (
auto mixer = videoMixer_) {
1584 JAMI_DBG(
"Stopping local camera sources");
1585 mixer->stopInputs();
1588 if (
auto mixer = videoMixer_) {
1589 JAMI_DBG(
"Starting local camera sources");
1591 for (
const auto& source : hostSources_) {
1614 if (
auto call = std::dynamic_pointer_cast<SIPCall>(
1617 auto recv = std::static_pointer_cast<video::VideoRtpSession>(
videoRtp)
1618 ->getVideoReceive();
1630 JAMI_WARN(
"Remote frame size not found.");
1636 for (
const auto& p : confInfo_) {
1659 JAMI_DBG(
"confInfo empty, remove remoteHost");
1660 std::lock_guard
lk(confInfoMutex_);
1662 sendConferenceInfos();
1672 if (
it != remoteHosts_.end()) {
1678 JAMI_WARN(
"No change in confInfo, don't update");
1689 videoMixer_->updateLayout();
1695Conference::findHostforRemoteParticipant(std::string_view uri, std::string_view deviceId)
1697 for (
const auto&
host : remoteHosts_) {
1706std::shared_ptr<Call>
1710 auto call = getCall(p);
1711 if (call && getRemoteId(call) ==
peerID) {
1718std::shared_ptr<Call>
1719Conference::getCallWith(
const std::string&
accountUri,
const std::string& deviceId)
1722 if (
auto call = std::dynamic_pointer_cast<SIPCall>(getCall(p))) {
1723 auto* transport = call->getTransport();
1725 && deviceId == transport->deviceId()) {
1734Conference::getRemoteId(
const std::shared_ptr<jami::Call>& call)
const
1736 if (
auto* transport = std::dynamic_pointer_cast<SIPCall>(call)->getTransport())
1737 if (
auto cert = transport->getTlsInfos().peerCert)
1739 return cert->issuer->getId().toString();
1761Conference::bindHostAudio()
1763 JAMI_LOG(
"Bind host to conference {}", id_);
1768 if (
auto call = getCall(
item)) {
1769 auto medias = call->getAudioStreams();
1771 for (
const auto& source : hostSources_) {
1777 .emplace(source.label_,
1778 std::make_shared<AudioInput>(source.label_))
1792 auto buffer = source.sourceUri_;
1794 const auto pos = source.sourceUri_.find(
sep);
1795 if (
pos != std::string::npos)
1810Conference::unbindHostAudio()
1812 JAMI_LOG(
"Unbind host from conference {}", id_);
1813 for (
const auto& source : hostSources_) {
1824 auto buffer = source.sourceUri_;
1826 const auto pos = source.sourceUri_.find(
sep);
1827 if (
pos != std::string::npos)
1837Conference::bindSubCallAudio(
const std::string& callId)
1839 JAMI_LOG(
"Bind participant {} to conference {}", callId, id_);
1875Conference::unbindSubCallAudio(
const std::string& callId)
1877 JAMI_LOG(
"Unbind participant {} from conference {}", callId, id_);
1878 if (
auto call = getCall(callId)) {
1879 auto medias = call->getAudioStreams();
std::shared_ptr< Call > getCall(const std::string &id) const
Return call pointer associated to given ID.Type can optionally be specified.
void onHangupParticipant(std::function< void(const std::string &, const std::string &)> &&cb)
void onMuteStreamAudio(std::function< void(const std::string &, const std::string &, const std::string &, bool)> &&cb)
void onKickParticipant(std::function< void(const std::string &)> &&cb)
void onRaiseHand(std::function< void(const std::string &, bool)> &&cb)
void initData(Json::Value &&d, std::string_view peerId)
Inject in the parser the data to parse.
void onMuteParticipant(std::function< void(const std::string &, bool)> &&cb)
void onSetActiveParticipant(std::function< void(const std::string &)> &&cb)
void onSetActiveStream(std::function< void(const std::string &, bool)> &&cb)
void onCheckAuthorization(std::function< bool(std::string_view)> &&cb)
Ask the caller to check if a peer is authorized (moderator of the conference)
void onSetLayout(std::function< void(int)> &&cb)
void onVersion(std::function< void(uint32_t)> &&cb)
void parse()
Parse the datas, this will call the methods injected if necessary.
void onVoiceActivity(std::function< void(const std::string &, bool)> &&cb)
void onRaiseHandUri(std::function< void(const std::string &, bool)> &&cb)
const char * getStateStr() const
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
bool startRecording(const std::string &path) override
Start recording.
const std::string & getConfId() const
Return the conference id.
void removeSubCall(const std::string &callId)
Remove a subcall from the conference.
void updateVoiceActivity()
std::vector< libjami::MediaMap > currentMediaList() const
Retrieve current medias list.
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
Conference(const std::shared_ptr< Account > &, const std::string &confId="")
Constructor for this class, increment static counter.
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)
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)
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.
Ring Account is build on top of SIPAccountBase and uses DHT to handle call connectivity.
Manager (controller) of daemon.
std::vector< std::shared_ptr< T > > getAllAccounts() const
Get a list of account pointers of type T (baseclass Account)
static LIBJAMI_TEST_EXPORT Manager & instance()
VideoManager * getVideoManager() const
bool hangupCall(const std::string &accountId, const std::string &callId)
Functions which occur with a user's action Hangup the call.
bool detachHost(const std::shared_ptr< Conference > &conf={})
Detach the local participant from curent conference.
RingBufferPool & getRingBufferPool()
Return a pointer to the instance of the RingBufferPool.
virtual bool startRecording(const std::string &path)
Start recording.
virtual void stopRecording()
Stop recording.
virtual bool toggleRecording()
This method must be implemented for this interface as calls and conferences have different behavior.
std::shared_ptr< MediaRecorder > recorder_
bool isRecording() const
Return recording state (true/false)
void unBindAllHalfDuplexIn(const std::string &sourceBufferId)
Detaches a source from all its readers.
static const char *const DEFAULT_ID
void unBindAll(const std::string &ringbufferId)
#define JAMI_ERROR(formatstr,...)
#define JAMI_DEBUG(formatstr,...)
#define JAMI_WARNING(formatstr,...)
#define JAMI_LOG(formatstr,...)
std::string toString(const Json::Value &jsonVal)
bool parse(std::string_view jsonStr, Json::Value &jsonVal)
std::string streamId(const std::string &callId, std::string_view label)
constexpr std::string_view DEFAULT_VIDEO_STREAMID
constexpr std::string_view DEFAULT_AUDIO_STREAMID
std::set< std::string > CallIdSet
static constexpr const char TRUE_STR[]
bool closeMediaPlayer(const std::string &id)
void emitSignal(Args... args)
std::shared_ptr< AudioInput > getAudioInput(const std::string &device)
std::vector< unsigned > split_string_to_unsigned(std::string_view str, char delim)
std::string_view string_remove_suffix(std::string_view str, char separator)
static constexpr const char FALSE_STR[]
std::string createMediaPlayer(const std::string &path)
static void runOnMainThread(Callback &&cb)
void hangupParticipant(const std::string &accountId, const std::string &confId, const std::string &accountUri, const std::string &deviceId)
SIPCall are SIP implementation of a normal Call.
Contains information about an AV subject.
std::string toString() const
std::vector< std::map< std::string, std::string > > toVectorMapStringString() const
#define jami_tracepoint(...)