Add the beginnings of an ICE socket implementation.
Currently, it can be demoed using --stun-test and the icedemo from pjsip as the other client. TODOs: Add ability to do a full demo using only 2 clementine instances. Complete session initiation over XMPP. Add local & port forwarded sockets as options.
This commit is contained in:
parent
cb763213bf
commit
18bfa12042
37
3rdparty/libxrme/connection.cpp
vendored
37
3rdparty/libxrme/connection.cpp
vendored
@ -47,7 +47,8 @@ struct Connection::Private : public gloox::ConnectionListener,
|
|||||||
public gloox::LogHandler,
|
public gloox::LogHandler,
|
||||||
public gloox::RosterListener,
|
public gloox::RosterListener,
|
||||||
public gloox::DiscoHandler,
|
public gloox::DiscoHandler,
|
||||||
public gloox::MessageHandler {
|
public gloox::MessageHandler,
|
||||||
|
public gloox::IqHandler {
|
||||||
Private(Connection* parent)
|
Private(Connection* parent)
|
||||||
: parent_(parent),
|
: parent_(parent),
|
||||||
server_(kDefaultServer),
|
server_(kDefaultServer),
|
||||||
@ -287,6 +288,9 @@ bool Connection::Connect() {
|
|||||||
d->client_->registerStanzaExtension(d->media_player_extension_);
|
d->client_->registerStanzaExtension(d->media_player_extension_);
|
||||||
d->client_->registerStanzaExtension(d->remote_control_extension_);
|
d->client_->registerStanzaExtension(d->remote_control_extension_);
|
||||||
d->client_->registerStanzaExtension(new MediaStorageExtension);
|
d->client_->registerStanzaExtension(new MediaStorageExtension);
|
||||||
|
d->client_->registerStanzaExtension(new SIPNegotiation);
|
||||||
|
|
||||||
|
d->client_->registerIqHandler(d.data(), SIPNegotiation::kExtensionType);
|
||||||
|
|
||||||
// Initialise the handlers
|
// Initialise the handlers
|
||||||
foreach (Handler* handler, d->handlers_) {
|
foreach (Handler* handler, d->handlers_) {
|
||||||
@ -302,7 +306,7 @@ bool Connection::Connect() {
|
|||||||
caps->setNode("http://tomahawk-player.org/");
|
caps->setNode("http://tomahawk-player.org/");
|
||||||
d->client_->presence().addExtension(caps);
|
d->client_->presence().addExtension(caps);
|
||||||
|
|
||||||
d->client_->setSASLMechanisms(gloox::SaslMechGoogleToken);
|
//d->client_->setSASLMechanisms(gloox::SaslMechGoogleToken);
|
||||||
|
|
||||||
// Connect
|
// Connect
|
||||||
if (!d->client_->connect(false)) {
|
if (!d->client_->connect(false)) {
|
||||||
@ -584,4 +588,33 @@ void Connection::Private::handleMessage(const gloox::Message& message, gloox::Me
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Connection::Private::handleIq(const gloox::IQ& iq) {
|
||||||
|
gloox::Tag* xrme = iq.tag()->findChild("xrme");
|
||||||
|
if (!xrme) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
gloox::Tag* sip = xrme->findChild("sip");
|
||||||
|
const std::string& user_fragment = sip->findAttribute("ufrag");
|
||||||
|
const std::string& password = sip->findAttribute("password");
|
||||||
|
|
||||||
|
gloox::TagList candidates = sip->findChildren("candidate");
|
||||||
|
for (gloox::TagList::const_iterator it = candidates.begin();
|
||||||
|
it != candidates.end(); ++it) {
|
||||||
|
gloox::Tag* candidate = *it;
|
||||||
|
const std::string& address = candidate->findAttribute("address");
|
||||||
|
const std::string& port = candidate->findAttribute("port");
|
||||||
|
const std::string& type = candidate->findAttribute("type");
|
||||||
|
const std::string& component = candidate->findAttribute("component");
|
||||||
|
const std::string& priority = candidate->findAttribute("priority");
|
||||||
|
const std::string& foundation = candidate->findAttribute("foundation");
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Connection::Private::handleIqID(const gloox::IQ& iq, int context) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace xrme
|
} // namespace xrme
|
||||||
|
16
3rdparty/libxrme/connection.h
vendored
16
3rdparty/libxrme/connection.h
vendored
@ -18,6 +18,7 @@
|
|||||||
#ifndef LIBXRME_CONNECTION_H
|
#ifndef LIBXRME_CONNECTION_H
|
||||||
#define LIBXRME_CONNECTION_H
|
#define LIBXRME_CONNECTION_H
|
||||||
|
|
||||||
|
#include <QHostAddress>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QPair>
|
#include <QPair>
|
||||||
#include <QScopedPointer>
|
#include <QScopedPointer>
|
||||||
@ -29,6 +30,21 @@ class MediaPlayerInterface;
|
|||||||
class MediaStorageInterface;
|
class MediaStorageInterface;
|
||||||
class RemoteControlInterface;
|
class RemoteControlInterface;
|
||||||
|
|
||||||
|
struct SIPInfo {
|
||||||
|
QString user_fragment;
|
||||||
|
QString password;
|
||||||
|
|
||||||
|
struct Candidate {
|
||||||
|
QHostAddress address;
|
||||||
|
quint16 port;
|
||||||
|
QString type;
|
||||||
|
int component;
|
||||||
|
int priority;
|
||||||
|
QString foundation;
|
||||||
|
};
|
||||||
|
QList<Candidate> candidates;
|
||||||
|
};
|
||||||
|
|
||||||
class Connection : public QObject {
|
class Connection : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
2
3rdparty/libxrme/extensions.cpp
vendored
2
3rdparty/libxrme/extensions.cpp
vendored
@ -8,5 +8,7 @@ const char* RemoteControlExtension::kFilterString =
|
|||||||
"/iq/xrme[@xmlns='http://purplehatstands.com/xmlns/xrme/remotecontrol']";
|
"/iq/xrme[@xmlns='http://purplehatstands.com/xmlns/xrme/remotecontrol']";
|
||||||
const char* MediaStorageExtension::kFilterString =
|
const char* MediaStorageExtension::kFilterString =
|
||||||
"/iq/xrme[@xmlns='http://purplehatstands.com/xmlns/xrme/mediastorage']";
|
"/iq/xrme[@xmlns='http://purplehatstands.com/xmlns/xrme/mediastorage']";
|
||||||
|
const char* SIPNegotiation::kFilterString =
|
||||||
|
"/iq/xrme[@xmlns='http://purplehatstands.com/xmlns/xrme/sipnegotiation']";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
6
3rdparty/libxrme/extensions.h
vendored
6
3rdparty/libxrme/extensions.h
vendored
@ -52,6 +52,12 @@ class MediaStorageExtension : public XRMEExtension<MediaStorageExtension> {
|
|||||||
static const char* kFilterString;
|
static const char* kFilterString;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class SIPNegotiation : public XRMEExtension<SIPNegotiation> {
|
||||||
|
public:
|
||||||
|
static const int kExtensionType = gloox::ExtUser + 4;
|
||||||
|
static const char* kFilterString;
|
||||||
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
XRMEExtension<T>::XRMEExtension()
|
XRMEExtension<T>::XRMEExtension()
|
||||||
: StanzaExtension(T::kExtensionType),
|
: StanzaExtension(T::kExtensionType),
|
||||||
|
@ -236,11 +236,15 @@ endif(ENABLE_VISUALISATIONS)
|
|||||||
option(STATIC_SQLITE "Compile and use a static sqlite3 library" ON)
|
option(STATIC_SQLITE "Compile and use a static sqlite3 library" ON)
|
||||||
|
|
||||||
pkg_check_modules(GNUTLS gnutls)
|
pkg_check_modules(GNUTLS gnutls)
|
||||||
if (GNUTLS_LIBRARIES)
|
if(GNUTLS_LIBRARIES)
|
||||||
set(HAVE_GNUTLS 1)
|
set(HAVE_GNUTLS 1)
|
||||||
endif (GNUTLS_LIBRARIES)
|
endif(GNUTLS_LIBRARIES)
|
||||||
|
pkg_check_modules(PJSIP libpjproject>=1.8)
|
||||||
|
if(PJSIP_LIBRARIES)
|
||||||
|
set(HAVE_PJSIP 1)
|
||||||
|
endif(PJSIP_LIBRARIES)
|
||||||
|
|
||||||
if(ENABLE_REMOTE AND HAVE_GNUTLS)
|
if(ENABLE_REMOTE AND HAVE_GNUTLS AND HAVE_PJSIP)
|
||||||
set(HAVE_REMOTE ON)
|
set(HAVE_REMOTE ON)
|
||||||
add_subdirectory(3rdparty/gloox)
|
add_subdirectory(3rdparty/gloox)
|
||||||
add_subdirectory(3rdparty/libxrme)
|
add_subdirectory(3rdparty/libxrme)
|
||||||
@ -248,7 +252,7 @@ if(ENABLE_REMOTE AND HAVE_GNUTLS)
|
|||||||
include_directories(3rdparty)
|
include_directories(3rdparty)
|
||||||
include_directories(3rdparty/libxrme)
|
include_directories(3rdparty/libxrme)
|
||||||
include_directories(3rdparty/libportfwd/include)
|
include_directories(3rdparty/libportfwd/include)
|
||||||
endif(ENABLE_REMOTE AND HAVE_GNUTLS)
|
endif(ENABLE_REMOTE AND HAVE_GNUTLS AND HAVE_PJSIP)
|
||||||
|
|
||||||
set(HAVE_STATIC_SQLITE ${STATIC_SQLITE})
|
set(HAVE_STATIC_SQLITE ${STATIC_SQLITE})
|
||||||
if(STATIC_SQLITE)
|
if(STATIC_SQLITE)
|
||||||
|
@ -43,6 +43,10 @@ if(HAVE_BREAKPAD)
|
|||||||
include_directories(../3rdparty/google-breakpad)
|
include_directories(../3rdparty/google-breakpad)
|
||||||
endif(HAVE_BREAKPAD)
|
endif(HAVE_BREAKPAD)
|
||||||
|
|
||||||
|
if(HAVE_REMOTE)
|
||||||
|
include_directories(${PJSIP_INCLUDE_DIRS})
|
||||||
|
endif(HAVE_REMOTE)
|
||||||
|
|
||||||
cmake_policy(SET CMP0011 NEW)
|
cmake_policy(SET CMP0011 NEW)
|
||||||
include(../cmake/ParseArguments.cmake)
|
include(../cmake/ParseArguments.cmake)
|
||||||
include(../cmake/Translations.cmake)
|
include(../cmake/Translations.cmake)
|
||||||
@ -792,11 +796,13 @@ endif(HAVE_SCRIPTING_PYTHON)
|
|||||||
|
|
||||||
if(HAVE_REMOTE)
|
if(HAVE_REMOTE)
|
||||||
list(APPEND HEADERS
|
list(APPEND HEADERS
|
||||||
|
remote/icesession.h
|
||||||
remote/portforwarder.h
|
remote/portforwarder.h
|
||||||
remote/remote.h
|
remote/remote.h
|
||||||
remote/remoteconfig.h
|
remote/remoteconfig.h
|
||||||
)
|
)
|
||||||
list(APPEND SOURCES
|
list(APPEND SOURCES
|
||||||
|
remote/icesession.cpp
|
||||||
remote/portforwarder.h
|
remote/portforwarder.h
|
||||||
remote/remote.cpp
|
remote/remote.cpp
|
||||||
remote/remoteconfig.cpp
|
remote/remoteconfig.cpp
|
||||||
@ -964,10 +970,12 @@ endif(HAVE_SCRIPTING_PYTHON)
|
|||||||
|
|
||||||
if(HAVE_REMOTE)
|
if(HAVE_REMOTE)
|
||||||
target_link_libraries(clementine_lib ${GLOOX_LIBRARIES})
|
target_link_libraries(clementine_lib ${GLOOX_LIBRARIES})
|
||||||
|
link_directories(${GLOOX_LIBRARY_DIRS})
|
||||||
target_link_libraries(clementine_lib xrme)
|
target_link_libraries(clementine_lib xrme)
|
||||||
target_link_libraries(clementine_lib portfwd)
|
target_link_libraries(clementine_lib portfwd)
|
||||||
target_link_libraries(clementine_lib qjson)
|
target_link_libraries(clementine_lib qjson)
|
||||||
link_directories(${GLOOX_LIBRARY_DIRS})
|
target_link_libraries(clementine_lib ${PJSIP_LIBRARIES})
|
||||||
|
link_directories(${PJSIP_LIBRARY_DIRS})
|
||||||
endif(HAVE_REMOTE)
|
endif(HAVE_REMOTE)
|
||||||
|
|
||||||
if(HAVE_BREAKPAD)
|
if(HAVE_BREAKPAD)
|
||||||
|
@ -61,7 +61,8 @@ CommandlineOptions::CommandlineOptions(int argc, char** argv)
|
|||||||
seek_to_(-1),
|
seek_to_(-1),
|
||||||
seek_by_(0),
|
seek_by_(0),
|
||||||
play_track_at_(-1),
|
play_track_at_(-1),
|
||||||
show_osd_(false)
|
show_osd_(false),
|
||||||
|
stun_test_(StunTestNone)
|
||||||
{
|
{
|
||||||
#ifdef Q_OS_DARWIN
|
#ifdef Q_OS_DARWIN
|
||||||
// Remove -psn_xxx option that Mac passes when opened from Finder.
|
// Remove -psn_xxx option that Mac passes when opened from Finder.
|
||||||
@ -108,6 +109,8 @@ bool CommandlineOptions::Parse() {
|
|||||||
{"show-osd", no_argument, 0, 'o'},
|
{"show-osd", no_argument, 0, 'o'},
|
||||||
{"language", required_argument, 0, 'g'},
|
{"language", required_argument, 0, 'g'},
|
||||||
|
|
||||||
|
{"stun-test", required_argument, 0, 'z'},
|
||||||
|
|
||||||
{0, 0, 0, 0}
|
{0, 0, 0, 0}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -180,6 +183,13 @@ bool CommandlineOptions::Parse() {
|
|||||||
if (!ok) play_track_at_ = -1;
|
if (!ok) play_track_at_ = -1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'z': {
|
||||||
|
// Stun test
|
||||||
|
QString direction = QString(optarg);
|
||||||
|
stun_test_ = direction == "offer" ? StunTestOffer : StunTestAccept;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case '?':
|
case '?':
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
|
@ -46,6 +46,11 @@ class CommandlineOptions {
|
|||||||
Player_Previous = 5,
|
Player_Previous = 5,
|
||||||
Player_Next = 6,
|
Player_Next = 6,
|
||||||
};
|
};
|
||||||
|
enum StunTestDirection {
|
||||||
|
StunTestNone = 0,
|
||||||
|
StunTestOffer = 1,
|
||||||
|
StunTestAccept = 2,
|
||||||
|
};
|
||||||
|
|
||||||
bool Parse();
|
bool Parse();
|
||||||
|
|
||||||
@ -62,6 +67,9 @@ class CommandlineOptions {
|
|||||||
QList<QUrl> urls() const { return urls_; }
|
QList<QUrl> urls() const { return urls_; }
|
||||||
QString language() const { return language_; }
|
QString language() const { return language_; }
|
||||||
|
|
||||||
|
StunTestDirection stun_test() const { return stun_test_; }
|
||||||
|
|
||||||
|
|
||||||
QByteArray Serialize() const;
|
QByteArray Serialize() const;
|
||||||
void Load(const QByteArray& serialized);
|
void Load(const QByteArray& serialized);
|
||||||
|
|
||||||
@ -93,6 +101,7 @@ class CommandlineOptions {
|
|||||||
int play_track_at_;
|
int play_track_at_;
|
||||||
bool show_osd_;
|
bool show_osd_;
|
||||||
QString language_;
|
QString language_;
|
||||||
|
StunTestDirection stun_test_;
|
||||||
|
|
||||||
QList<QUrl> urls_;
|
QList<QUrl> urls_;
|
||||||
};
|
};
|
||||||
|
65
src/main.cpp
65
src/main.cpp
@ -130,6 +130,9 @@ void GLog(const gchar* domain,
|
|||||||
qDebug() << "GLOG" << message;
|
qDebug() << "GLOG" << message;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#include <xrme/connection.h>
|
||||||
|
#include "remote/icesession.h"
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
if (CrashReporting::SendCrashReport(argc, argv)) {
|
if (CrashReporting::SendCrashReport(argc, argv)) {
|
||||||
return 0;
|
return 0;
|
||||||
@ -213,6 +216,8 @@ int main(int argc, char *argv[]) {
|
|||||||
qRegisterMetaType<GstElement*>("GstElement*");
|
qRegisterMetaType<GstElement*>("GstElement*");
|
||||||
qRegisterMetaType<GstEnginePipeline*>("GstEnginePipeline*");
|
qRegisterMetaType<GstEnginePipeline*>("GstEnginePipeline*");
|
||||||
|
|
||||||
|
qRegisterMetaType<xrme::SIPInfo>("SIPInfo");
|
||||||
|
|
||||||
#ifdef HAVE_LIBLASTFM
|
#ifdef HAVE_LIBLASTFM
|
||||||
lastfm::ws::ApiKey = LastFMService::kApiKey;
|
lastfm::ws::ApiKey = LastFMService::kApiKey;
|
||||||
lastfm::ws::SharedSecret = LastFMService::kSecret;
|
lastfm::ws::SharedSecret = LastFMService::kSecret;
|
||||||
@ -248,6 +253,66 @@ int main(int argc, char *argv[]) {
|
|||||||
UniversalEncodingHandler handler;
|
UniversalEncodingHandler handler;
|
||||||
TagLib::ID3v1::Tag::setStringHandler(&handler);
|
TagLib::ID3v1::Tag::setStringHandler(&handler);
|
||||||
|
|
||||||
|
if (options.stun_test() != CommandlineOptions::StunTestNone) {
|
||||||
|
QCoreApplication app(argc, argv);
|
||||||
|
|
||||||
|
ICESession::StaticInit();
|
||||||
|
ICESession ice;
|
||||||
|
ice.Init();
|
||||||
|
|
||||||
|
QEventLoop loop;
|
||||||
|
QObject::connect(&ice,
|
||||||
|
SIGNAL(CandidatesAvailable(const SessionInfo&)),
|
||||||
|
&loop, SLOT(quit()));
|
||||||
|
loop.exec();
|
||||||
|
|
||||||
|
const xrme::SIPInfo& candidates = ice.candidates();
|
||||||
|
qDebug() << candidates;
|
||||||
|
|
||||||
|
QFile file;
|
||||||
|
file.open(stdin, QIODevice::ReadOnly);
|
||||||
|
QTextStream in(&file);
|
||||||
|
|
||||||
|
xrme::SIPInfo remote_session;
|
||||||
|
qDebug() << "fragment";
|
||||||
|
in >> remote_session.user_fragment;
|
||||||
|
qDebug() << "password";
|
||||||
|
in >> remote_session.password;
|
||||||
|
|
||||||
|
xrme::SIPInfo::Candidate cand;
|
||||||
|
QString address;
|
||||||
|
qDebug() << "address";
|
||||||
|
in >> address;
|
||||||
|
cand.address = address;
|
||||||
|
qDebug() << "port";
|
||||||
|
in >> cand.port;
|
||||||
|
QString type;
|
||||||
|
qDebug() << "type";
|
||||||
|
in >> type;
|
||||||
|
if (type == "host") {
|
||||||
|
cand.type = PJ_ICE_CAND_TYPE_HOST;
|
||||||
|
} else if (type == "srflx") {
|
||||||
|
cand.type = PJ_ICE_CAND_TYPE_SRFLX;
|
||||||
|
} else if (type == "prflx") {
|
||||||
|
cand.type = PJ_ICE_CAND_TYPE_PRFLX;
|
||||||
|
}
|
||||||
|
qDebug() << "component";
|
||||||
|
in >> cand.component;
|
||||||
|
qDebug() << "priority";
|
||||||
|
in >> cand.priority;
|
||||||
|
qDebug() << "foundation";
|
||||||
|
in >> cand.foundation;
|
||||||
|
|
||||||
|
remote_session.candidates << cand;
|
||||||
|
|
||||||
|
qDebug() << "Remote:" << remote_session;
|
||||||
|
|
||||||
|
ice.StartNegotiation(remote_session);
|
||||||
|
loop.exec();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
QtSingleApplication a(argc, argv);
|
QtSingleApplication a(argc, argv);
|
||||||
#ifdef Q_OS_DARWIN
|
#ifdef Q_OS_DARWIN
|
||||||
QCoreApplication::setLibraryPaths(
|
QCoreApplication::setLibraryPaths(
|
||||||
|
280
src/remote/icesession.cpp
Normal file
280
src/remote/icesession.cpp
Normal file
@ -0,0 +1,280 @@
|
|||||||
|
#include "icesession.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) {
|
||||||
|
qWarning() << "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) {
|
||||||
|
qWarning() << "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();
|
||||||
|
}
|
64
src/remote/icesession.h
Normal file
64
src/remote/icesession.h
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
#ifndef ICESESSION_H
|
||||||
|
#define ICESESSION_H
|
||||||
|
|
||||||
|
#include <pjlib.h>
|
||||||
|
#include <pjlib-util.h>
|
||||||
|
#include <pjnath.h>
|
||||||
|
|
||||||
|
#include <QHostAddress>
|
||||||
|
#include <QList>
|
||||||
|
#include <QtDebug>
|
||||||
|
|
||||||
|
#include <xrme/connection.h>
|
||||||
|
|
||||||
|
|
||||||
|
class ICESession : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit ICESession(QObject* parent = 0);
|
||||||
|
|
||||||
|
static void StaticInit();
|
||||||
|
bool Init();
|
||||||
|
|
||||||
|
const xrme::SIPInfo& candidates() const { return candidates_; }
|
||||||
|
|
||||||
|
void StartNegotiation(const xrme::SIPInfo& session);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void CandidatesAvailable(const xrme::SIPInfo& candidates);
|
||||||
|
|
||||||
|
private:
|
||||||
|
pj_ice_strans* ice_instance_;
|
||||||
|
int component_id_;
|
||||||
|
|
||||||
|
xrme::SIPInfo candidates_;
|
||||||
|
|
||||||
|
void InitialisationComplete(pj_status_t status);
|
||||||
|
|
||||||
|
|
||||||
|
static int HandleEvents(unsigned max_msec, unsigned* p_count);
|
||||||
|
static int WorkerThread(void*);
|
||||||
|
|
||||||
|
static void 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);
|
||||||
|
static void OnICEComplete(pj_ice_strans* ice_st,
|
||||||
|
pj_ice_strans_op op,
|
||||||
|
pj_status_t status);
|
||||||
|
|
||||||
|
static pj_pool_t* sPool;
|
||||||
|
static pj_caching_pool sCachingPool;
|
||||||
|
static pj_ice_strans_cfg sIceConfig;
|
||||||
|
static pj_thread_t* sThread;
|
||||||
|
|
||||||
|
static int sComponentId;
|
||||||
|
|
||||||
|
static const char* kStunServer;
|
||||||
|
};
|
||||||
|
|
||||||
|
QDebug operator<< (QDebug dbg, const xrme::SIPInfo& session);
|
||||||
|
|
||||||
|
#endif // ICESESSION_H
|
@ -62,7 +62,7 @@ void Remote::ReloadSettings() {
|
|||||||
s.beginGroup(RemoteConfig::kSettingsGroup);
|
s.beginGroup(RemoteConfig::kSettingsGroup);
|
||||||
|
|
||||||
QString username = s.value("username").toString();
|
QString username = s.value("username").toString();
|
||||||
QString password = s.value("token").toString();
|
QString password = s.value("password").toString();
|
||||||
QString agent_name = s.value("agent_name", RemoteConfig::DefaultAgentName()).toString();
|
QString agent_name = s.value("agent_name", RemoteConfig::DefaultAgentName()).toString();
|
||||||
|
|
||||||
// Have the settings changed?
|
// Have the settings changed?
|
||||||
|
Loading…
x
Reference in New Issue
Block a user