Commit 3c85f036 authored by Sébastien Blin's avatar Sébastien Blin

conference: add a rendez-vous mode

This patch aims to be a first iteration on a new conference mode. Actually,
creating a conference for many participants is pretty boring and the UX is
bad because for any conference, we need to call any participant and add them.

This new mode transform an account into a meeting account. This means whenever
this account receives a call (from an authorized peer) the call is answered
and added to the current conference. So, an account can easily host a meeting.

Gitlab: #269
Change-Id: I5ece585f8eb46f5cf6a5f9e3dc56829949cc7a81
parent d0774ee3
......@@ -78,6 +78,7 @@ const char * const Account::PASSWORD_KEY = "password";
const char * const Account::HOSTNAME_KEY = "hostname";
const char * const Account::ACCOUNT_ENABLE_KEY = "enable";
const char * const Account::ACCOUNT_AUTOANSWER_KEY = "autoAnswer";
const char * const Account::ACCOUNT_ISRENDEZVOUS_KEY = "rendezVous";
const char * const Account::ACCOUNT_ACTIVE_CALL_LIMIT_KEY = "activeCallLimit";
const char * const Account::MAILBOX_KEY = "mailbox";
const char * const Account::DEFAULT_USER_AGENT = PACKAGE_NAME;
......@@ -101,6 +102,7 @@ Account::Account(const std::string &accountID)
, alias_()
, enabled_(true)
, autoAnswerEnabled_(false)
, isRendezVous_(false)
, registrationState_(RegistrationState::UNREGISTERED)
, systemCodecContainer_(getSystemCodecContainer())
, accountCodecInfoList_()
......@@ -227,6 +229,7 @@ Account::serialize(YAML::Emitter& out) const
out << YAML::Key << ACTIVE_CODEC_KEY << YAML::Value << activeCodecs;
out << YAML::Key << MAILBOX_KEY << YAML::Value << mailBox_;
out << YAML::Key << ACCOUNT_AUTOANSWER_KEY << YAML::Value << autoAnswerEnabled_;
out << YAML::Key << ACCOUNT_ISRENDEZVOUS_KEY << YAML::Value << isRendezVous_;
out << YAML::Key << ACCOUNT_ACTIVE_CALL_LIMIT_KEY << YAML::Value << activeCallLimit_;
out << YAML::Key << RINGTONE_ENABLED_KEY << YAML::Value << ringtoneEnabled_;
out << YAML::Key << RINGTONE_PATH_KEY << YAML::Value << ringtonePath_;
......@@ -246,6 +249,7 @@ Account::unserialize(const YAML::Node& node)
parseValue(node, ALIAS_KEY, alias_);
parseValue(node, ACCOUNT_ENABLE_KEY, enabled_);
parseValue(node, ACCOUNT_AUTOANSWER_KEY, autoAnswerEnabled_);
parseValueOptional(node, ACCOUNT_ISRENDEZVOUS_KEY, isRendezVous_);
parseValue(node, ACCOUNT_ACTIVE_CALL_LIMIT_KEY, activeCallLimit_);
//parseValue(node, PASSWORD_KEY, password_);
......@@ -298,6 +302,7 @@ Account::setAccountDetails(const std::map<std::string, std::string> &details)
parseString(details, Conf::CONFIG_ACCOUNT_MAILBOX, mailBox_);
parseString(details, Conf::CONFIG_ACCOUNT_USERAGENT, userAgent_);
parseBool(details, Conf::CONFIG_ACCOUNT_AUTOANSWER, autoAnswerEnabled_);
parseBool(details, Conf::CONFIG_ACCOUNT_ISRENDEZVOUS, isRendezVous_);
parseInt(details, DRing::Account::ConfProperties::ACTIVE_CALL_LIMIT, activeCallLimit_);
parseBool(details, Conf::CONFIG_RINGTONE_ENABLED, ringtoneEnabled_);
parseString(details, Conf::CONFIG_RINGTONE_PATH, ringtonePath_);
......@@ -328,6 +333,7 @@ Account::getAccountDetails() const
{Conf::CONFIG_ACCOUNT_USERAGENT, hasCustomUserAgent_ ? userAgent_ : DEFAULT_USER_AGENT},
{Conf::CONFIG_ACCOUNT_HAS_CUSTOM_USERAGENT, hasCustomUserAgent_ ? userAgent_ : DEFAULT_USER_AGENT},
{Conf::CONFIG_ACCOUNT_AUTOANSWER, autoAnswerEnabled_ ? TRUE_STR : FALSE_STR},
{Conf::CONFIG_ACCOUNT_ISRENDEZVOUS, isRendezVous_ ? TRUE_STR : FALSE_STR},
{DRing::Account::ConfProperties::ACTIVE_CALL_LIMIT, std::to_string(activeCallLimit_)},
{Conf::CONFIG_RINGTONE_ENABLED, ringtoneEnabled_ ? TRUE_STR : FALSE_STR},
{Conf::CONFIG_RINGTONE_PATH, ringtonePath_},
......
......@@ -181,8 +181,6 @@ class Account : public Serializable, public std::enable_shared_from_this<Account
return false;
}
std::vector<std::shared_ptr<Call>> getCalls();
/**
* Tell if the account is enable or not.
* @return true if enabled, false otherwise
......@@ -289,6 +287,10 @@ class Account : public Serializable, public std::enable_shared_from_this<Account
mailBox_ = mb;
}
bool isRendezVous() const {
return isRendezVous_;
}
void attachCall(const std::string& id);
void detachCall(const std::string& id);
......@@ -384,6 +386,7 @@ class Account : public Serializable, public std::enable_shared_from_this<Account
static const char * const HOSTNAME_KEY;
static const char * const ACCOUNT_ENABLE_KEY;
static const char * const ACCOUNT_AUTOANSWER_KEY;
static const char * const ACCOUNT_ISRENDEZVOUS_KEY;
static const char * const ACCOUNT_ACTIVE_CALL_LIMIT_KEY;
static const char * const MAILBOX_KEY;
static const char * const USER_AGENT_KEY;
......@@ -437,6 +440,9 @@ class Account : public Serializable, public std::enable_shared_from_this<Account
/* If true, automatically answer calls to this account */
bool autoAnswerEnabled_;
/* If true mix calls into a conference */
bool isRendezVous_;
/**
* The number of concurrent calls for the account
* -1: Unlimited
......
......@@ -36,6 +36,7 @@ static const char *const CONFIG_ACCOUNT_DISPLAYNAME = "Account.displ
static const char *const CONFIG_ACCOUNT_MAILBOX = "Account.mailbox";
static const char *const CONFIG_ACCOUNT_ENABLE = "Account.enable";
static const char *const CONFIG_ACCOUNT_AUTOANSWER = "Account.autoAnswer";
static const char *const CONFIG_ACCOUNT_ISRENDEZVOUS = "Account.rendezVous";
static const char *const CONFIG_ACCOUNT_REGISTRATION_EXPIRE = "Account.registrationExpire";
static const char *const CONFIG_ACCOUNT_DTMF_TYPE = "Account.dtmfType";
static const char *const CONFIG_RINGTONE_PATH = "Account.ringtonePath";
......
......@@ -115,6 +115,7 @@ constexpr static const char ENABLED [] = "Account.enable";
constexpr static const char MAILBOX [] = "Account.mailbox";
constexpr static const char DTMF_TYPE [] = "Account.dtmfType";
constexpr static const char AUTOANSWER [] = "Account.autoAnswer";
constexpr static const char ISRENDEZVOUS [] = "Account.rendezVous";
constexpr static const char ACTIVE_CALL_LIMIT [] = "Account.activeCallLimit";
constexpr static const char HOSTNAME [] = "Account.hostname";
constexpr static const char USERNAME [] = "Account.username";
......
......@@ -43,6 +43,7 @@
#include "string_utils.h"
#include "jamidht/jamiaccount.h"
#include "sip/sipvoiplink.h"
#include "account.h"
#include <opendht/rng.h>
using random_device = dht::crypto::random_device;
......@@ -535,11 +536,17 @@ Manager::ManagerPimpl::processRemainingParticipants(Conference& conf)
auto p = participants.begin();
if (auto call = base_.getCallFromCallID(*p)) {
call->setConfId("");
// if we are not listening to this conference
if (current_call_id != conf.getConfID())
base_.onHoldCall(call->getCallId());
else
switchCall(call);
// if we are not listening to this conference and not a rendez-vous
auto isRdv = false;
if (auto acc = std::dynamic_pointer_cast<JamiAccount>(base_.getAccount(call->getAccountId())))
isRdv = acc->getAccountDetails()[Conf::CONFIG_ACCOUNT_ISRENDEZVOUS] == TRUE_STR;
if (!isRdv) {
if (current_call_id != conf.getConfID())
base_.onHoldCall(call->getCallId());
else
switchCall(call);
}
}
JAMI_DBG("No remaining participants, remove conference");
......@@ -1344,6 +1351,10 @@ Manager::addParticipant(const std::string& callId,
pimpl_->bindCallToConference(*call, *conf);
// Don't attach current user yet
if (conf->getState() == Conference::State::ACTIVE_DETACHED)
return true;
// TODO: remove this ugly hack => There should be different calls when double clicking
// a conference to add main participant to it, or (in this case) adding a participant
// toconference
......@@ -1384,7 +1395,7 @@ Manager::getCallFromCallID(const std::string& callID) const
}
bool
Manager::joinParticipant(const std::string& callId1, const std::string& callId2)
Manager::joinParticipant(const std::string& callId1, const std::string& callId2, bool attached)
{
if (callId1 == callId2) {
JAMI_ERR("Cannot join participant %s to itself", callId1.c_str());
......@@ -1412,8 +1423,12 @@ Manager::joinParticipant(const std::string& callId1, const std::string& callId2)
pimpl_->bindCallToConference(*call2, *conf);
// Switch current call id to this conference
pimpl_->switchCall(conf->getConfID());
conf->setState(Conference::State::ACTIVE_ATTACHED);
if (attached) {
pimpl_->switchCall(conf->getConfID());
conf->setState(Conference::State::ACTIVE_ATTACHED);
} else {
conf->detach();
}
pimpl_->conferenceMap_.emplace(conf->getConfID(), conf);
emitSignal<DRing::CallSignal::ConferenceCreated>(conf->getConfID());
......@@ -1836,8 +1851,42 @@ Manager::incomingCall(Call &call, const std::string& accountId)
emitSignal<DRing::CallSignal::IncomingCall>(accountId, callID, call.getPeerDisplayName() + " " + from);
auto currentCall = getCurrentCall();
if (pimpl_->autoAnswer_) {
runOnMainThread([this, callID]{ answerCall(callID); });
if (call.getAccount().isRendezVous()) {
runOnMainThread([this, callID] {
answerCall(callID);
auto call = getCallFromCallID(callID);
auto accountId = call->getAccountId();
for (const auto& cid: getCallList()) {
if (auto call = getCallFromCallID(cid)) {
if (call->getState() != Call::CallState::ACTIVE)
continue;
if (call->getAccountId() == accountId) {
if (cid != callID) {
if (call->getConfId().empty()) {
joinParticipant(callID, cid, false);
} else {
addParticipant(callID, call->getConfId());
}
return;
}
}
}
}
// First call
auto conf = std::make_shared<Conference>();
// Bind calls according to their state
pimpl_->bindCallToConference(*call, *conf);
conf->detach();
pimpl_->conferenceMap_.emplace(conf->getConfID(), conf);
emitSignal<DRing::CallSignal::ConferenceCreated>(conf->getConfID());
});
} else if (pimpl_->autoAnswer_) {
runOnMainThread([this, callID]{
answerCall(callID);
});
} else if (currentCall) {
// Test if already calling this person
if (currentCall->getAccountId() == accountId
......
......@@ -285,7 +285,8 @@ class DRING_TESTABLE Manager {
* @param the second call id
*/
bool joinParticipant(const std::string& call_id1,
const std::string& call_id2);
const std::string& call_id2,
bool attached = true);
/**
* Create a conference from a list of participant
......
......@@ -333,15 +333,16 @@ VideoRtpSession::enterConference(Conference* conference)
JAMI_DBG("[call:%s] enterConference (conf: %s)", callID_.c_str(),
conference->getConfID().c_str());
if (send_.enabled or receiveThread_) {
videoMixer_ = conference->getVideoMixer();
// TODO is this correct? The video Mixer should be enabled for a detached conference even if we are not sending values
videoMixer_ = conference->getVideoMixer();
#if defined(__APPLE__) && TARGET_OS_MAC
videoMixer_->setParameters(localVideoParams_.width,
localVideoParams_.height,
av_get_pix_fmt(localVideoParams_.pixel_format.c_str()));
videoMixer_->setParameters(localVideoParams_.width,
localVideoParams_.height,
av_get_pix_fmt(localVideoParams_.pixel_format.c_str()));
#else
videoMixer_->setParameters(localVideoParams_.width, localVideoParams_.height);
videoMixer_->setParameters(localVideoParams_.width, localVideoParams_.height);
#endif
if (send_.enabled or receiveThread_) {
setupConferenceVideoPipeline(*conference_);
// Restart encoder with conference parameter ON in order to unlink HW encoder
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment