Bläddra i källkod

Added state with corresponding callback, and removed optional candidates

Paul-Louis Ageneau 5 år sedan
förälder
incheckning
86179f0691

+ 2 - 1
include/rtc/description.hpp

@@ -47,7 +47,8 @@ public:
 
 	void setFingerprint(string fingerprint);
 	void setSctpPort(uint16_t port);
-	void addCandidate(std::optional<Candidate> candidate);
+	void addCandidate(Candidate candidate);
+	void endCandidates();
 
 	operator string() const;
 

+ 24 - 5
include/rtc/peerconnection.hpp

@@ -20,12 +20,13 @@
 #define RTC_PEER_CONNECTION_H
 
 #include "candidate.hpp"
+#include "configuration.hpp"
 #include "datachannel.hpp"
 #include "description.hpp"
-#include "configuration.hpp"
 #include "include.hpp"
 #include "message.hpp"
 #include "reliability.hpp"
+#include "rtc.hpp"
 
 #include <atomic>
 #include <functional>
@@ -40,12 +41,23 @@ class SctpTransport;
 
 class PeerConnection {
 public:
+	enum class State : int {
+		New = RTC_NEW,
+		Gathering = RTC_GATHERING,
+		Finished = RTC_FINISHED,
+		Connecting = RTC_CONNECTING,
+		Connected = RTC_CONNECTED,
+		Disconnected = RTC_DISCONNECTED,
+		Failed = RTC_FAILED,
+		Closed = RTC_CLOSED
+	};
+
 	PeerConnection(void);
 	PeerConnection(const Configuration &config);
 	~PeerConnection();
 
 	const Configuration *config() const;
-
+	State state() const;
 	std::optional<Description> localDescription() const;
 	std::optional<Description> remoteDescription() const;
 
@@ -57,7 +69,8 @@ public:
 
 	void onDataChannel(std::function<void(std::shared_ptr<DataChannel> dataChannel)> callback);
 	void onLocalDescription(std::function<void(const Description &description)> callback);
-	void onLocalCandidate(std::function<void(const std::optional<Candidate> &candidate)> callback);
+	void onLocalCandidate(std::function<void(const Candidate &candidate)> callback);
+	void onStateChanged(std::function<void(State state)> callback);
 
 private:
 	void initIceTransport(Description::Role role);
@@ -71,8 +84,9 @@ private:
 	void closeDataChannels();
 
 	void processLocalDescription(Description description);
-	void processLocalCandidate(std::optional<Candidate> candidate);
+	void processLocalCandidate(Candidate candidate);
 	void triggerDataChannel(std::shared_ptr<DataChannel> dataChannel);
+	void changeState(State state);
 
 	const Configuration mConfig;
 	const std::shared_ptr<Certificate> mCertificate;
@@ -86,11 +100,16 @@ private:
 
 	std::unordered_map<unsigned int, std::weak_ptr<DataChannel>> mDataChannels;
 
+	std::atomic<State> mState;
+
 	std::function<void(std::shared_ptr<DataChannel> dataChannel)> mDataChannelCallback;
 	std::function<void(const Description &description)> mLocalDescriptionCallback;
-	std::function<void(const std::optional<Candidate> &candidate)> mLocalCandidateCallback;
+	std::function<void(const Candidate &candidate)> mLocalCandidateCallback;
+	std::function<void(State state)> mStateChangedCallback;
 };
 
 } // namespace rtc
 
+std::ostream &operator<<(std::ostream &out, const rtc::PeerConnection::State &state);
+
 #endif

+ 18 - 0
include/rtc/rtc.h

@@ -16,11 +16,26 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
+#ifndef RTC_C_API
+#define RTC_C_API
+
 #ifdef __cplusplus
 extern "C" {
 #endif
 
 // libdatachannel rtc C API
+
+typedef enum {
+	RTC_NEW = 0,
+	RTC_GATHERING = 1,
+	RTC_FINISHED = 2,
+	RTC_CONNECTING = 3,
+	RTC_CONNECTED = 4,
+	RTC_DISCONNECTED = 5,
+	RTC_FAILED = 6,
+	RTC_CLOSED = 7
+} rtc_state_t;
+
 int rtcCreatePeerConnection(const char **iceServers, int iceServersCount);
 void rtcDeletePeerConnection(int pc);
 int rtcCreateDataChannel(int pc, const char *label);
@@ -30,6 +45,7 @@ void rtcSetLocalDescriptionCallback(int pc, void (*descriptionCallback)(const ch
                                                                         void *));
 void rtcSetLocalCandidateCallback(int pc,
                                   void (*candidateCallback)(const char *, const char *, void *));
+void rtcSetStateChangedCallback(int pc, void (*stateCallback)(rtc_state_t state, void *));
 void rtcSetRemoteDescription(int pc, const char *sdp, const char *type);
 void rtcAddRemoteCandidate(int pc, const char *candidate, const char *mid);
 int rtcGetDataChannelLabel(int dc, char *data, int size);
@@ -43,3 +59,5 @@ void rtcSetUserPointer(int i, void *ptr);
 } // extern "C"
 #endif
 
+#endif
+

+ 4 - 5
src/description.cpp

@@ -109,13 +109,12 @@ void Description::setFingerprint(string fingerprint) {
 
 void Description::setSctpPort(uint16_t port) { mSctpPort.emplace(port); }
 
-void Description::addCandidate(std::optional<Candidate> candidate) {
-	if (candidate)
-		mCandidates.emplace_back(std::move(*candidate));
-	else
-		mTrickle = false;
+void Description::addCandidate(Candidate candidate) {
+	mCandidates.emplace_back(std::move(candidate));
 }
 
+void Description::endCandidates() { mTrickle = false; }
+
 Description::operator string() const {
 	if (!mFingerprint)
 		throw std::logic_error("Fingerprint must be set to generate a SDP");

+ 23 - 4
src/dtlstransport.cpp

@@ -47,9 +47,11 @@ namespace rtc {
 using std::shared_ptr;
 
 DtlsTransport::DtlsTransport(shared_ptr<IceTransport> lower, shared_ptr<Certificate> certificate,
-                             verifier_callback verifier, ready_callback ready)
-    : Transport(lower), mCertificate(certificate), mVerifierCallback(std::move(verifier)),
-      mReadyCallback(std::move(ready)) {
+                             verifier_callback verifierCallback,
+                             state_callback stateChangedCallback)
+    : Transport(lower), mCertificate(certificate), mState(State::Disconnected),
+      mVerifierCallback(std::move(verifierCallback)),
+      mStateChangedCallback(std::move(stateChangedCallback)) {
 	gnutls_certificate_set_verify_function(mCertificate->credentials(), CertificateCallback);
 
 	bool active = lower->role() == Description::Role::Active;
@@ -82,6 +84,8 @@ DtlsTransport::~DtlsTransport() {
 	gnutls_deinit(mSession);
 }
 
+DtlsTransport::State DtlsTransport::state() const { return mState; }
+
 bool DtlsTransport::send(message_ptr message) {
 	if (!message)
 		return false;
@@ -96,12 +100,25 @@ bool DtlsTransport::send(message_ptr message) {
 
 void DtlsTransport::incoming(message_ptr message) { mIncomingQueue.push(message); }
 
+void DtlsTransport::changeState(State state) {
+	mState = state;
+	mStateChangedCallback(state);
+}
+
 void DtlsTransport::runRecvLoop() {
 	try {
+		changeState(State::Connecting);
+
 		while (!check_gnutls(gnutls_handshake(mSession), "TLS handshake failed")) {
 		}
+	} catch (const std::exception &e) {
+		std::cerr << "DTLS handshake: " << e.what() << std::endl;
+		changeState(State::Failed);
+		return;
+	}
 
-		mReadyCallback();
+	try {
+		changeState(State::Connected);
 
 		const size_t bufferSize = 2048;
 		char buffer[bufferSize];
@@ -117,10 +134,12 @@ void DtlsTransport::runRecvLoop() {
 				recv(make_message(b, b + ret));
 			}
 		}
+
 	} catch (const std::exception &e) {
 		std::cerr << "DTLS recv: " << e.what() << std::endl;
 	}
 
+	changeState(State::Disconnected);
 	recv(nullptr);
 }
 

+ 11 - 3
src/dtlstransport.hpp

@@ -25,6 +25,7 @@
 #include "queue.hpp"
 #include "transport.hpp"
 
+#include <atomic>
 #include <functional>
 #include <memory>
 #include <thread>
@@ -37,26 +38,33 @@ class IceTransport;
 
 class DtlsTransport : public Transport {
 public:
+	enum class State { Disconnected, Connecting, Connected, Failed };
+
 	using verifier_callback = std::function<bool(const std::string &fingerprint)>;
-	using ready_callback = std::function<void(void)>;
+	using state_callback = std::function<void(State state)>;
 
 	DtlsTransport(std::shared_ptr<IceTransport> lower, std::shared_ptr<Certificate> certificate,
-	              verifier_callback verifier, ready_callback ready);
+	              verifier_callback verifierCallback, state_callback stateChangedCallback);
 	~DtlsTransport();
 
+	State state() const;
+
 	bool send(message_ptr message);
 
 private:
 	void incoming(message_ptr message);
+	void changeState(State state);
 	void runRecvLoop();
 
 	const std::shared_ptr<Certificate> mCertificate;
 
 	gnutls_session_t mSession;
 	Queue<message_ptr> mIncomingQueue;
+	std::atomic<State> mState;
 	std::thread mRecvThread;
+
 	verifier_callback mVerifierCallback;
-	ready_callback mReadyCallback;
+	state_callback mStateChangedCallback;
 
 	static int CertificateCallback(gnutls_session_t session);
 	static ssize_t WriteCallback(gnutls_transport_ptr_t ptr, const void *data, size_t len);

+ 9 - 6
src/icetransport.cpp

@@ -34,10 +34,11 @@ using std::shared_ptr;
 using std::weak_ptr;
 
 IceTransport::IceTransport(const Configuration &config, Description::Role role,
-                           candidate_callback candidateCallback, ready_callback ready)
+                           candidate_callback candidateCallback,
+                           state_callback stateChangedCallback)
     : mRole(role), mMid("0"), mState(State::Disconnected), mNiceAgent(nullptr, nullptr),
       mMainLoop(nullptr, nullptr), mCandidateCallback(std::move(candidateCallback)),
-      mReadyCallback(ready) {
+      mStateChangedCallback(std::move(stateChangedCallback)) {
 
 	auto logLevelFlags = GLogLevelFlags(G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION);
 	g_log_set_handler(nullptr, logLevelFlags, LogCallback, this);
@@ -189,13 +190,15 @@ void IceTransport::processCandidate(const string &candidate) {
 	mCandidateCallback(Candidate(candidate, mMid));
 }
 
-void IceTransport::processGatheringDone() { mCandidateCallback(nullopt); }
+void IceTransport::processGatheringDone() {
+	if (mState == State::Gathering) {
+		mState = State::Finished;
+	}
+}
 
 void IceTransport::changeState(uint32_t state) {
 	mState = static_cast<State>(state);
-	if (mState == State::Ready) {
-		mReadyCallback();
-	}
+	mStateChangedCallback(mState);
 }
 
 void IceTransport::CandidateCallback(NiceAgent *agent, NiceCandidate *candidate,

+ 6 - 6
src/icetransport.hpp

@@ -31,7 +31,6 @@ extern "C" {
 }
 
 #include <atomic>
-#include <optional>
 #include <thread>
 
 namespace rtc {
@@ -41,17 +40,18 @@ public:
 	enum class State : uint32_t {
 		Disconnected = NICE_COMPONENT_STATE_DISCONNECTED,
 		Gathering = NICE_COMPONENT_STATE_GATHERING,
+		Finished = static_cast<uint32_t>(NICE_COMPONENT_STATE_LAST) + 1,
 		Connecting = NICE_COMPONENT_STATE_CONNECTING,
 		Connected = NICE_COMPONENT_STATE_CONNECTED,
 		Ready = NICE_COMPONENT_STATE_READY,
 		Failed = NICE_COMPONENT_STATE_FAILED
 	};
 
-	using candidate_callback = std::function<void(const std::optional<Candidate> &candidate)>;
-	using ready_callback = std::function<void(void)>;
+	using candidate_callback = std::function<void(const Candidate &candidate)>;
+	using state_callback = std::function<void(State state)>;
 
 	IceTransport(const Configuration &config, Description::Role role,
-	             candidate_callback candidateCallback, ready_callback ready);
+	             candidate_callback candidateCallback, state_callback stateChangedCallback);
 	~IceTransport();
 
 	Description::Role role() const;
@@ -74,7 +74,7 @@ private:
 
 	Description::Role mRole;
 	string mMid;
-	State mState;
+	std::atomic<State> mState;
 
 	uint32_t mStreamId = 0;
 	std::unique_ptr<NiceAgent, void (*)(gpointer)> mNiceAgent;
@@ -82,7 +82,7 @@ private:
 	std::thread mMainLoopThread;
 
 	candidate_callback mCandidateCallback;
-	ready_callback mReadyCallback;
+	state_callback mStateChangedCallback;
 
 	static void CandidateCallback(NiceAgent *agent, NiceCandidate *candidate, gpointer userData);
 	static void GatheringDoneCallback(NiceAgent *agent, guint streamId, gpointer userData);

+ 105 - 10
src/peerconnection.cpp

@@ -34,12 +34,14 @@ using std::shared_ptr;
 PeerConnection::PeerConnection() : PeerConnection(Configuration()) {}
 
 PeerConnection::PeerConnection(const Configuration &config)
-    : mConfig(config), mCertificate(make_certificate("libdatachannel")) {}
+    : mConfig(config), mCertificate(make_certificate("libdatachannel")), mState(State::New) {}
 
 PeerConnection::~PeerConnection() {}
 
 const Configuration *PeerConnection::config() const { return &mConfig; }
 
+PeerConnection::State PeerConnection::state() const { return mState; }
+
 std::optional<Description> PeerConnection::localDescription() const { return mLocalDescription; }
 
 std::optional<Description> PeerConnection::remoteDescription() const { return mRemoteDescription; }
@@ -62,7 +64,7 @@ void PeerConnection::addRemoteCandidate(Candidate candidate) {
 		throw std::logic_error("Remote candidate set without remote description");
 
 	if (mIceTransport->addRemoteCandidate(candidate))
-		mRemoteDescription->addCandidate(std::make_optional(std::move(candidate)));
+		mRemoteDescription->addCandidate(std::move(candidate));
 }
 
 shared_ptr<DataChannel> PeerConnection::createDataChannel(const string &label,
@@ -86,7 +88,7 @@ shared_ptr<DataChannel> PeerConnection::createDataChannel(const string &label,
 		initIceTransport(Description::Role::Active);
 		processLocalDescription(mIceTransport->getLocalDescription(Description::Type::Offer));
 		mIceTransport->gatherLocalCandidates();
-	} else if (mSctpTransport && mSctpTransport->isReady()) {
+	} else if (mSctpTransport && mSctpTransport->state() == SctpTransport::State::Connected) {
 		channel->open(mSctpTransport);
 	}
 	return channel;
@@ -102,28 +104,82 @@ void PeerConnection::onLocalDescription(
 	mLocalDescriptionCallback = callback;
 }
 
-void PeerConnection::onLocalCandidate(
-    std::function<void(const std::optional<Candidate> &candidate)> callback) {
+void PeerConnection::onLocalCandidate(std::function<void(const Candidate &candidate)> callback) {
 	mLocalCandidateCallback = callback;
 }
 
+void PeerConnection::onStateChanged(std::function<void(State state)> callback) {
+	mStateChangedCallback = callback;
+}
+
 void PeerConnection::initIceTransport(Description::Role role) {
 	mIceTransport = std::make_shared<IceTransport>(
 	    mConfig, role, std::bind(&PeerConnection::processLocalCandidate, this, _1),
-	    std::bind(&PeerConnection::initDtlsTransport, this));
+	    [this](IceTransport::State state) {
+		    switch (state) {
+		    case IceTransport::State::Gathering:
+			    changeState(State::Gathering);
+			    break;
+		    case IceTransport::State::Finished:
+			    if (mLocalDescription)
+				    mLocalDescription->endCandidates();
+			    changeState(State::Finished);
+			    break;
+		    case IceTransport::State::Connecting:
+			    changeState(State::Connecting);
+			    break;
+		    case IceTransport::State::Failed:
+			    changeState(State::Failed);
+			    break;
+		    case IceTransport::State::Ready:
+			    initDtlsTransport();
+			    break;
+		    default:
+			    // Ignore
+			    break;
+		    }
+	    });
 }
 
 void PeerConnection::initDtlsTransport() {
 	mDtlsTransport = std::make_shared<DtlsTransport>(
 	    mIceTransport, mCertificate, std::bind(&PeerConnection::checkFingerprint, this, _1),
-	    std::bind(&PeerConnection::initSctpTransport, this));
+	    [this](DtlsTransport::State state) {
+		    switch (state) {
+		    case DtlsTransport::State::Connected:
+			    initSctpTransport();
+			    break;
+		    case DtlsTransport::State::Failed:
+			    changeState(State::Failed);
+			    break;
+		    default:
+			    // Ignore
+			    break;
+		    }
+	    });
 }
 
 void PeerConnection::initSctpTransport() {
 	uint16_t sctpPort = mRemoteDescription->sctpPort().value_or(DEFAULT_SCTP_PORT);
 	mSctpTransport = std::make_shared<SctpTransport>(
-	    mDtlsTransport, sctpPort, std::bind(&PeerConnection::openDataChannels, this),
-	    std::bind(&PeerConnection::forwardMessage, this, _1));
+	    mDtlsTransport, sctpPort, std::bind(&PeerConnection::forwardMessage, this, _1),
+	    [this](SctpTransport::State state) {
+		    switch (state) {
+		    case SctpTransport::State::Connected:
+			    changeState(State::Connected);
+			    openDataChannels();
+			    break;
+		    case SctpTransport::State::Failed:
+			    changeState(State::Failed);
+			    break;
+		    case SctpTransport::State::Disconnected:
+			    changeState(State::Disconnected);
+			    break;
+		    default:
+			    // Ignore
+			    break;
+		    }
+	    });
 }
 
 bool PeerConnection::checkFingerprint(const std::string &fingerprint) const {
@@ -203,7 +259,7 @@ void PeerConnection::processLocalDescription(Description description) {
 		mLocalDescriptionCallback(*mLocalDescription);
 }
 
-void PeerConnection::processLocalCandidate(std::optional<Candidate> candidate) {
+void PeerConnection::processLocalCandidate(Candidate candidate) {
 	if (!mLocalDescription)
 		throw std::logic_error("Got a local candidate without local description");
 
@@ -218,4 +274,43 @@ void PeerConnection::triggerDataChannel(std::shared_ptr<DataChannel> dataChannel
 		mDataChannelCallback(dataChannel);
 }
 
+void PeerConnection::changeState(State state) {
+	mState = state;
+	if (mStateChangedCallback)
+		mStateChangedCallback(state);
+}
+
 } // namespace rtc
+
+std::ostream &operator<<(std::ostream &out, const rtc::PeerConnection::State &state) {
+	using namespace rtc;
+	string str;
+	switch (state) {
+	case PeerConnection::State::New:
+		str = "new";
+		break;
+	case PeerConnection::State::Gathering:
+		str = "gathering";
+		break;
+	case PeerConnection::State::Finished:
+		str = "finished";
+		break;
+	case PeerConnection::State::Connecting:
+		str = "connecting";
+		break;
+	case PeerConnection::State::Connected:
+		str = "connected";
+		break;
+	case PeerConnection::State::Disconnected:
+		str = "disconnected";
+		break;
+	case PeerConnection::State::Failed:
+		str = "failed";
+		break;
+	default:
+		str = "unknown";
+		break;
+	}
+	return out << str;
+}
+

+ 14 - 9
src/rtc.cpp

@@ -95,15 +95,20 @@ void rtcSetLocalCandidateCallback(int pc,
 	if (it == peerConnectionMap.end())
 		return;
 
-	it->second->onLocalCandidate(
-	    [pc, candidateCallback](const std::optional<Candidate> &candidate) {
-		    if (candidate) {
-			    candidateCallback(string(*candidate).c_str(), candidate->mid().c_str(),
-			                      getUserPointer(pc));
-		    } else {
-			    candidateCallback(nullptr, nullptr, getUserPointer(pc));
-		    }
-	    });
+	it->second->onLocalCandidate([pc, candidateCallback](const Candidate &candidate) {
+		candidateCallback(candidate.candidate().c_str(), candidate.mid().c_str(),
+		                  getUserPointer(pc));
+	});
+}
+
+void rtcSetStateChangedCallback(int pc, void (*stateCallback)(rtc_state_t state, void *)) {
+	auto it = peerConnectionMap.find(pc);
+	if (it == peerConnectionMap.end())
+		return;
+
+	it->second->onStateChanged([pc, stateCallback](PeerConnection::State state) {
+		stateCallback(static_cast<rtc_state_t>(state), getUserPointer(pc));
+	});
 }
 
 void rtcSetRemoteDescription(int pc, const char *sdp, const char *type) {

+ 17 - 8
src/sctptransport.cpp

@@ -47,9 +47,10 @@ void SctpTransport::GlobalCleanup() {
 	}
 }
 
-SctpTransport::SctpTransport(std::shared_ptr<Transport> lower, uint16_t port, ready_callback ready,
-                             message_callback recv)
-    : Transport(lower), mReadyCallback(std::move(ready)), mPort(port) {
+SctpTransport::SctpTransport(std::shared_ptr<Transport> lower, uint16_t port, message_callback recv,
+                             state_callback stateChangedCallback)
+    : Transport(lower), mPort(port), mState(State::Disconnected),
+      mStateChangedCallback(std::move(stateChangedCallback)) {
 
 	onRecv(recv);
 
@@ -133,7 +134,7 @@ SctpTransport::~SctpTransport() {
 	GlobalCleanup();
 }
 
-bool SctpTransport::isReady() const { return mIsReady; }
+SctpTransport::State SctpTransport::state() const { return mState; }
 
 bool SctpTransport::send(message_ptr message) {
 	if (!message)
@@ -206,6 +207,7 @@ void SctpTransport::reset(unsigned int stream) {
 
 void SctpTransport::incoming(message_ptr message) {
 	if (!message) {
+		changeState(State::Disconnected);
 		recv(nullptr);
 		return;
 	}
@@ -223,8 +225,15 @@ void SctpTransport::incoming(message_ptr message) {
 		usrsctp_conninput(this, message->data(), message->size(), 0);
 }
 
+void SctpTransport::changeState(State state) {
+	mState = state;
+	mStateChangedCallback(state);
+}
+
 void SctpTransport::runConnect() {
 	try {
+		changeState(State::Connecting);
+
 		struct sockaddr_conn sconn = {};
 		sconn.sconn_family = AF_CONN;
 		sconn.sconn_port = htons(mPort);
@@ -239,14 +248,14 @@ void SctpTransport::runConnect() {
 		if (usrsctp_connect(mSock, reinterpret_cast<struct sockaddr *>(&sconn), sizeof(sconn)) !=
 		    0) {
 			std::cerr << "SCTP connection failed, errno=" << errno << std::endl;
+			changeState(State::Failed);
 			mStopping = true;
 			return;
 		}
 
-		if (!mStopping) {
-			mIsReady = true;
-			mReadyCallback();
-		}
+		if (!mStopping)
+			changeState(State::Connected);
+
 	} catch (const std::exception &e) {
 		std::cerr << "SCTP connect: " << e.what() << std::endl;
 	}

+ 12 - 9
src/sctptransport.hpp

@@ -36,13 +36,15 @@ namespace rtc {
 
 class SctpTransport : public Transport {
 public:
-	using ready_callback = std::function<void(void)>;
+	enum class State { Disconnected, Connecting, Connected, Failed };
 
-	SctpTransport(std::shared_ptr<Transport> lower, uint16_t port, ready_callback ready,
-	              message_callback recv);
+	using state_callback = std::function<void(State state)>;
+
+	SctpTransport(std::shared_ptr<Transport> lower, uint16_t port, message_callback recv,
+	              state_callback stateChangedCallback);
 	~SctpTransport();
 
-	bool isReady() const;
+	State state() const;
 
 	bool send(message_ptr message);
 	void reset(unsigned int stream);
@@ -57,6 +59,7 @@ private:
 	};
 
 	void incoming(message_ptr message);
+	void changeState(State state);
 	void runConnect();
 
 	int handleWrite(void *data, size_t len, uint8_t tos, uint8_t set_df);
@@ -67,18 +70,18 @@ private:
 	void processData(const byte *data, size_t len, uint16_t streamId, PayloadId ppid);
 	void processNotification(const union sctp_notification *notify, size_t len);
 
-	ready_callback mReadyCallback;
-
 	struct socket *mSock;
 	uint16_t mPort;
 
 	std::thread mConnectThread;
-	std::atomic<bool> mStopping = false;
-	std::atomic<bool> mIsReady = false;
-
 	std::mutex mConnectMutex;
 	std::condition_variable mConnectCondition;
 	std::atomic<bool> mConnectDataSent = false;
+	std::atomic<bool> mStopping = false;
+
+	std::atomic<State> mState;
+
+	state_callback mStateChangedCallback;
 
 	static int WriteCallback(void *sctp_ptr, void *data, size_t len, uint8_t tos, uint8_t set_df);
 	static int ReadCallback(struct socket *sock, union sctp_sockstore addr, void *data, size_t len,

+ 10 - 10
test/main.cpp

@@ -38,25 +38,25 @@ int main(int argc, char **argv) {
 		pc2->setRemoteDescription(sdp);
 	});
 
-	pc1->onLocalCandidate([pc2](const optional<Candidate> &candidate) {
-		if (candidate) {
-			cout << "Candidate 1: " << *candidate << endl;
-			pc2->addRemoteCandidate(*candidate);
-		}
+	pc1->onLocalCandidate([pc2](const Candidate &candidate) {
+		cout << "Candidate 1: " << candidate << endl;
+		pc2->addRemoteCandidate(candidate);
 	});
 
+	pc1->onStateChanged([](PeerConnection::State state) { cout << "State 1: " << state << endl; });
+
 	pc2->onLocalDescription([pc1](const Description &sdp) {
 		cout << "Description 2: " << sdp << endl;
 		pc1->setRemoteDescription(sdp);
 	});
 
-	pc2->onLocalCandidate([pc1](const optional<Candidate> &candidate) {
-		if (candidate) {
-			cout << "Candidate 2: " << *candidate << endl;
-			pc1->addRemoteCandidate(*candidate);
-		}
+	pc2->onLocalCandidate([pc1](const Candidate &candidate) {
+		cout << "Candidate 2: " << candidate << endl;
+		pc1->addRemoteCandidate(candidate);
 	});
 
+	pc2->onStateChanged([](PeerConnection::State state) { cout << "State 2: " << state << endl; });
+
 	shared_ptr<DataChannel> dc2;
 	pc2->onDataChannel([&dc2](shared_ptr<DataChannel> dc) {
 		cout << "Got a DataChannel with label: " << dc->label() << endl;