Clementine-audio-player-Mac.../src/remote/icesession.cpp

282 lines
7.6 KiB
C++

#include "icesession.h"
#include "core/logging.h"
#include <QHostAddress>
using xrme::SIPInfo;
const char* ICESession::kStunServer = "stunserver.org";
int ICESession::sComponentId = 0;
pj_caching_pool ICESession::sCachingPool;
pj_ice_strans_cfg ICESession::sIceConfig;
pj_pool_t* ICESession::sPool;
pj_thread_t* ICESession::sThread;
ICESession::ICESession(QObject* parent)
: QObject(parent) {
}
bool ICESession::Init() {
// Create instance.
pj_ice_strans_cb ice_cb;
pj_bzero(&ice_cb, sizeof(ice_cb));
ice_cb.on_rx_data = &OnReceiveData;
ice_cb.on_ice_complete = &OnICEComplete;
component_id_ = ++sComponentId;
pj_status_t status = pj_ice_strans_create(
"clementine",
&sIceConfig,
component_id_,
this,
&ice_cb,
&ice_instance_);
if (status != PJ_SUCCESS) {
qLog(Warning) << "Failed to create ICE instance";
return false;
}
// TODO
pj_ice_sess_role role = PJ_ICE_SESS_ROLE_CONTROLLING;
status = pj_ice_strans_init_ice(ice_instance_, role, NULL, NULL);
qDebug() << "Init ice:" << status;
return true;
}
QString CandidateTypeToString(pj_ice_cand_type type) {
switch (type) {
case PJ_ICE_CAND_TYPE_HOST:
return "host";
case PJ_ICE_CAND_TYPE_SRFLX:
return "srflx";
case PJ_ICE_CAND_TYPE_PRFLX:
return "prflx";
case PJ_ICE_CAND_TYPE_RELAYED:
return "relayed";
}
}
pj_ice_cand_type CandidateStringToType(const QString& type) {
if (type == "host") {
return PJ_ICE_CAND_TYPE_HOST;
} else if (type == "srflx") {
return PJ_ICE_CAND_TYPE_SRFLX;
} else if (type == "prflx") {
return PJ_ICE_CAND_TYPE_PRFLX;
} else if (type == "relayed") {
return PJ_ICE_CAND_TYPE_RELAYED;
}
return PJ_ICE_CAND_TYPE_HOST;
}
void ICESession::InitialisationComplete(pj_status_t status) {
int candidates = pj_ice_strans_get_cands_count(ice_instance_, component_id_);
pj_ice_sess_cand cand[candidates];
pj_ice_strans_get_def_cand(ice_instance_, component_id_, &cand[0]);
pj_str_t ufrag;
pj_str_t pwd;
pj_ice_strans_get_ufrag_pwd(ice_instance_, &ufrag, &pwd, NULL, NULL);
candidates_.user_fragment = QString::fromAscii(ufrag.ptr, ufrag.slen);
candidates_.password = QString::fromAscii(pwd.ptr, pwd.slen);
for (int i = 0; i < candidates; ++i) {
int port = pj_sockaddr_get_port(&cand[i].addr);
char ipaddr[PJ_INET6_ADDRSTRLEN];
pj_sockaddr_print(&cand[i].addr, ipaddr, sizeof(ipaddr), 0);
QHostAddress address(QString::fromAscii(ipaddr));
SIPInfo::Candidate candidate;
candidate.address = address;
candidate.port = port;
candidate.type = CandidateTypeToString(cand[i].type);
candidate.component = component_id_;
candidate.priority = cand[i].prio;
candidate.foundation = QString::fromAscii(
cand[i].foundation.ptr, cand[i].foundation.slen);
candidates_.candidates << candidate;
}
emit CandidatesAvailable(candidates_);
}
void ICESession::StartNegotiation(const xrme::SIPInfo& session) {
pj_str_t remote_ufrag;
pj_str_t remote_password;
pj_cstr(&remote_ufrag, strdup(session.user_fragment.toAscii().constData()));
pj_cstr(&remote_password, strdup(session.password.toAscii().constData()));
pj_ice_sess_cand candidates[session.candidates.size()];
for (int i = 0; i < session.candidates.size(); ++i) {
const xrme::SIPInfo::Candidate c = session.candidates[i];
pj_ice_sess_cand* candidate = &candidates[i];
pj_bzero(candidate, sizeof(*candidate));
candidate->type = CandidateStringToType(c.type);
candidate->comp_id = c.component;
candidate->prio = c.priority;
int af = c.address.protocol() == QAbstractSocket::IPv6Protocol
? pj_AF_INET6()
: pj_AF_INET();
pj_sockaddr_init(af, &candidate->addr, NULL, 0);
pj_str_t temp_addr;
pj_cstr(&temp_addr, c.address.toString().toAscii().constData());
pj_sockaddr_set_str_addr(af, &candidate->addr, &temp_addr);
pj_sockaddr_set_port(&candidate->addr, c.port);
pj_cstr(&candidate->foundation, c.foundation.toAscii().constData());
}
pj_status_t status = pj_ice_strans_start_ice(
ice_instance_,
&remote_ufrag,
&remote_password,
session.candidates.size(),
candidates);
if (status != PJ_SUCCESS) {
qLog(Warning) << "Start negotation failed";
} else {
qDebug() << "ICE negotiation started";
}
}
void ICESession::OnReceiveData(pj_ice_strans* ice_st,
unsigned comp_id,
void* pkt,
pj_size_t size,
const pj_sockaddr_t* src_addr,
unsigned src_addr_len) {
qDebug() << "Received data";
}
void ICESession::OnICEComplete(pj_ice_strans* ice_st,
pj_ice_strans_op op,
pj_status_t status) {
ICESession* me = reinterpret_cast<ICESession*>(pj_ice_strans_get_user_data(ice_st));
const char* op_name = NULL;
switch (op) {
case PJ_ICE_STRANS_OP_INIT:
op_name = "initialisation";
me->InitialisationComplete(status);
break;
case PJ_ICE_STRANS_OP_NEGOTIATION:
op_name = "negotation";
break;
default:
op_name = "unknown";
}
qDebug() << op_name << (status == PJ_SUCCESS ? "succeeded" : "failed");
}
int ICESession::HandleEvents(unsigned max_msec, unsigned* p_count) {
pj_time_val max_timeout = { 0, 0 };
max_timeout.msec = max_msec;
pj_time_val timeout = { 0, 0 };
int c = pj_timer_heap_poll(sIceConfig.stun_cfg.timer_heap, &timeout);
int count = 0;
if (c > 0) {
count += c;
}
if (timeout.msec >= 1000) {
timeout.msec = 999;
}
if (PJ_TIME_VAL_GT(timeout, max_timeout)) {
timeout = max_timeout;
}
static const int kMaxNetEvents = 1;
int net_event_count = 0;
do {
c = pj_ioqueue_poll(sIceConfig.stun_cfg.ioqueue, &timeout);
if (c < 0) {
pj_status_t err = pj_get_netos_error();
pj_thread_sleep(PJ_TIME_VAL_MSEC(timeout));
if (p_count) {
*p_count = count;
}
return err;
} else if (c == 0) {
break;
} else {
net_event_count += c;
timeout.sec = 0;
timeout.msec = 0;
}
} while (c > 0 && net_event_count < kMaxNetEvents);
count += net_event_count;
if (p_count) {
*p_count = count;
}
return PJ_SUCCESS;
}
int ICESession::WorkerThread(void*) {
forever {
HandleEvents(500, NULL);
}
return 0;
}
void ICESession::StaticInit() {
pj_init();
pjlib_util_init();
pjnath_init();
pj_caching_pool_init(&sCachingPool, NULL, 0);
pj_ice_strans_cfg_default(&sIceConfig);
sIceConfig.stun_cfg.pf = &sCachingPool.factory;
sPool = pj_pool_create(&sCachingPool.factory, "clementine", 512, 512, NULL);
pj_timer_heap_create(sPool, 100, &sIceConfig.stun_cfg.timer_heap);
pj_ioqueue_create(sPool, 16, &sIceConfig.stun_cfg.ioqueue);
pj_thread_create(sPool, "clementine", &WorkerThread, NULL, 0, 0, &sThread);
sIceConfig.af = pj_AF_INET();
sIceConfig.stun.server.ptr = strdup(kStunServer);
sIceConfig.stun.server.slen = strlen(kStunServer);
sIceConfig.stun.port = PJ_STUN_PORT;
}
QDebug operator<< (QDebug dbg, const xrme::SIPInfo& session) {
dbg.nospace() << "fragment:" << session.user_fragment << "\n";
dbg.nospace() << "password:" << session.password << "\n";
foreach (const xrme::SIPInfo::Candidate& c, session.candidates) {
dbg.space() << "Candidate:" << "\n";
dbg.space() << c.address.toString() << ":" << c.port << "\n";
dbg.space() << "type:" << c.type << "\n";
dbg.space() << "component:" << c.component << "\n";
dbg.space() << "priority:" << c.priority << "\n";
dbg.space() << "foundation:" << c.foundation << "\n";
}
return dbg.space();
}