Skip to content

Commit

Permalink
Merge pull request #944 from paullouisageneau/ice-state
Browse files Browse the repository at this point in the history
Add ICE state to API
  • Loading branch information
paullouisageneau authored Aug 12, 2023
2 parents 9cfb6dd + 8eb20ad commit 9b0f18c
Show file tree
Hide file tree
Showing 11 changed files with 157 additions and 5 deletions.
13 changes: 13 additions & 0 deletions include/rtc/peerconnection.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,16 @@ class RTC_CPP_EXPORT PeerConnection final : CheshireCat<impl::PeerConnection> {
Closed = RTC_CLOSED
};

enum class IceState : int {
New = RTC_ICE_NEW,
Checking = RTC_ICE_CHECKING,
Connected = RTC_ICE_CONNECTED,
Completed = RTC_ICE_COMPLETED,
Failed = RTC_ICE_FAILED,
Disconnected = RTC_ICE_DISCONNECTED,
Closed = RTC_ICE_CLOSED
};

enum class GatheringState : int {
New = RTC_GATHERING_NEW,
InProgress = RTC_GATHERING_INPROGRESS,
Expand All @@ -68,6 +78,7 @@ class RTC_CPP_EXPORT PeerConnection final : CheshireCat<impl::PeerConnection> {

const Configuration *config() const;
State state() const;
IceState iceState() const;
GatheringState gatheringState() const;
SignalingState signalingState() const;
bool hasMedia() const;
Expand All @@ -94,6 +105,7 @@ class RTC_CPP_EXPORT PeerConnection final : CheshireCat<impl::PeerConnection> {
void onLocalDescription(std::function<void(Description description)> callback);
void onLocalCandidate(std::function<void(Candidate candidate)> callback);
void onStateChange(std::function<void(State state)> callback);
void onIceStateChange(std::function<void(IceState state)> callback);
void onGatheringStateChange(std::function<void(GatheringState state)> callback);
void onSignalingStateChange(std::function<void(SignalingState state)> callback);

Expand All @@ -109,6 +121,7 @@ class RTC_CPP_EXPORT PeerConnection final : CheshireCat<impl::PeerConnection> {
} // namespace rtc

RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, rtc::PeerConnection::State state);
RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out, rtc::PeerConnection::IceState state);
RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out,
rtc::PeerConnection::GatheringState state);
RTC_CPP_EXPORT std::ostream &operator<<(std::ostream &out,
Expand Down
12 changes: 12 additions & 0 deletions include/rtc/rtc.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,16 @@ typedef enum {
RTC_CLOSED = 5
} rtcState;

typedef enum {
RTC_ICE_NEW = 0,
RTC_ICE_CHECKING = 1,
RTC_ICE_CONNECTED = 2,
RTC_ICE_COMPLETED = 3,
RTC_ICE_FAILED = 4,
RTC_ICE_DISCONNECTED = 5,
RTC_ICE_CLOSED = 6
} rtcIceState;

typedef enum {
RTC_GATHERING_NEW = 0,
RTC_GATHERING_INPROGRESS = 1,
Expand Down Expand Up @@ -131,6 +141,7 @@ typedef void(RTC_API *rtcDescriptionCallbackFunc)(int pc, const char *sdp, const
typedef void(RTC_API *rtcCandidateCallbackFunc)(int pc, const char *cand, const char *mid,
void *ptr);
typedef void(RTC_API *rtcStateChangeCallbackFunc)(int pc, rtcState state, void *ptr);
typedef void(RTC_API *rtcIceStateChangeCallbackFunc)(int pc, rtcIceState state, void *ptr);
typedef void(RTC_API *rtcGatheringStateCallbackFunc)(int pc, rtcGatheringState state, void *ptr);
typedef void(RTC_API *rtcSignalingStateCallbackFunc)(int pc, rtcSignalingState state, void *ptr);
typedef void(RTC_API *rtcDataChannelCallbackFunc)(int pc, int dc, void *ptr);
Expand Down Expand Up @@ -179,6 +190,7 @@ RTC_C_EXPORT int rtcDeletePeerConnection(int pc);
RTC_C_EXPORT int rtcSetLocalDescriptionCallback(int pc, rtcDescriptionCallbackFunc cb);
RTC_C_EXPORT int rtcSetLocalCandidateCallback(int pc, rtcCandidateCallbackFunc cb);
RTC_C_EXPORT int rtcSetStateChangeCallback(int pc, rtcStateChangeCallbackFunc cb);
RTC_C_EXPORT int rtcSetIceStateChangeCallback(int pc, rtcIceStateChangeCallbackFunc cb);
RTC_C_EXPORT int rtcSetGatheringStateChangeCallback(int pc, rtcGatheringStateCallbackFunc cb);
RTC_C_EXPORT int rtcSetSignalingStateChangeCallback(int pc, rtcSignalingStateCallbackFunc cb);

Expand Down
14 changes: 14 additions & 0 deletions src/capi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,20 @@ int rtcSetStateChangeCallback(int pc, rtcStateChangeCallbackFunc cb) {
});
}

int rtcSetIceStateChangeCallback(int pc, rtcIceStateChangeCallbackFunc cb) {
return wrap([&] {
auto peerConnection = getPeerConnection(pc);
if (cb)
peerConnection->onIceStateChange([pc, cb](PeerConnection::IceState state) {
if (auto ptr = getUserPointer(pc))
cb(pc, static_cast<rtcIceState>(state), *ptr);
});
else
peerConnection->onIceStateChange(nullptr);
return RTC_ERR_SUCCESS;
});
}

int rtcSetGatheringStateChangeCallback(int pc, rtcGatheringStateCallbackFunc cb) {
return wrap([&] {
auto peerConnection = getPeerConnection(pc);
Expand Down
31 changes: 30 additions & 1 deletion src/impl/peerconnection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,16 +152,23 @@ shared_ptr<IceTransport> PeerConnection::initIceTransport() {
return;
switch (transportState) {
case IceTransport::State::Connecting:
changeIceState(IceState::Checking);
changeState(State::Connecting);
break;
case IceTransport::State::Connected:
changeIceState(IceState::Connected);
initDtlsTransport();
break;
case IceTransport::State::Completed:
changeIceState(IceState::Completed);
break;
case IceTransport::State::Failed:
changeIceState(IceState::Failed);
changeState(State::Failed);
mProcessor.enqueue(&PeerConnection::remoteClose, shared_from_this());
break;
case IceTransport::State::Disconnected:
changeIceState(IceState::Disconnected);
changeState(State::Disconnected);
mProcessor.enqueue(&PeerConnection::remoteClose, shared_from_this());
break;
Expand Down Expand Up @@ -345,11 +352,14 @@ shared_ptr<SctpTransport> PeerConnection::getSctpTransport() const {
void PeerConnection::closeTransports() {
PLOG_VERBOSE << "Closing transports";

// Change ICE state to sink state Closed
changeIceState(IceState::Closed);

// Change state to sink state Closed
if (!changeState(State::Closed))
return; // already closed

// Reset intercceptor and callbacks now that state is changed
// Reset interceptor and callbacks now that state is changed
setMediaHandler(nullptr);
resetCallbacks();

Expand Down Expand Up @@ -1175,6 +1185,24 @@ bool PeerConnection::changeState(State newState) {
return true;
}

bool PeerConnection::changeIceState(IceState newState) {
if (iceState.exchange(newState) == newState)
return false;

std::ostringstream s;
s << newState;
PLOG_INFO << "Changed ICE state to " << s.str();

if (newState == IceState::Closed) {
auto callback = std::move(iceStateChangeCallback); // steal the callback
callback(IceState::Closed); // call it synchronously
} else {
mProcessor.enqueue(&PeerConnection::trigger<IceState>, shared_from_this(),
&iceStateChangeCallback, newState);
}
return true;
}

bool PeerConnection::changeGatheringState(GatheringState newState) {
if (gatheringState.exchange(newState) == newState)
return false;
Expand Down Expand Up @@ -1207,6 +1235,7 @@ void PeerConnection::resetCallbacks() {
localDescriptionCallback = nullptr;
localCandidateCallback = nullptr;
stateChangeCallback = nullptr;
iceStateChangeCallback = nullptr;
gatheringStateChangeCallback = nullptr;
signalingStateChangeCallback = nullptr;
trackCallback = nullptr;
Expand Down
4 changes: 4 additions & 0 deletions src/impl/peerconnection.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ namespace rtc::impl {

struct PeerConnection : std::enable_shared_from_this<PeerConnection> {
using State = rtc::PeerConnection::State;
using IceState = rtc::PeerConnection::IceState;
using GatheringState = rtc::PeerConnection::GatheringState;
using SignalingState = rtc::PeerConnection::SignalingState;

Expand Down Expand Up @@ -92,6 +93,7 @@ struct PeerConnection : std::enable_shared_from_this<PeerConnection> {
void flushPendingTracks();

bool changeState(State newState);
bool changeIceState(IceState newState);
bool changeGatheringState(GatheringState newState);
bool changeSignalingState(SignalingState newState);

Expand All @@ -108,6 +110,7 @@ struct PeerConnection : std::enable_shared_from_this<PeerConnection> {

const Configuration config;
std::atomic<State> state = State::New;
std::atomic<IceState> iceState = IceState::New;
std::atomic<GatheringState> gatheringState = GatheringState::New;
std::atomic<SignalingState> signalingState = SignalingState::Stable;
std::atomic<bool> negotiationNeeded = false;
Expand All @@ -118,6 +121,7 @@ struct PeerConnection : std::enable_shared_from_this<PeerConnection> {
synchronized_callback<Description> localDescriptionCallback;
synchronized_callback<Candidate> localCandidateCallback;
synchronized_callback<State> stateChangeCallback;
synchronized_callback<IceState> iceStateChangeCallback;
synchronized_callback<GatheringState> gatheringStateChangeCallback;
synchronized_callback<SignalingState> signalingStateChangeCallback;
synchronized_callback<shared_ptr<rtc::Track>> trackCallback;
Expand Down
40 changes: 40 additions & 0 deletions src/peerconnection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ const Configuration *PeerConnection::config() const { return &impl()->config; }

PeerConnection::State PeerConnection::state() const { return impl()->state; }

PeerConnection::IceState PeerConnection::iceState() const {
return impl()->iceState;
}

PeerConnection::GatheringState PeerConnection::gatheringState() const {
return impl()->gatheringState;
}
Expand Down Expand Up @@ -311,6 +315,10 @@ void PeerConnection::onStateChange(std::function<void(State state)> callback) {
impl()->stateChangeCallback = callback;
}

void PeerConnection::onIceStateChange(std::function<void(IceState state)> callback) {
impl()->iceStateChangeCallback = callback;
}

void PeerConnection::onGatheringStateChange(std::function<void(GatheringState state)> callback) {
impl()->gatheringStateChangeCallback = callback;
}
Expand Down Expand Up @@ -377,6 +385,38 @@ std::ostream &operator<<(std::ostream &out, rtc::PeerConnection::State state) {
return out << str;
}

std::ostream &operator<<(std::ostream &out, rtc::PeerConnection::IceState state) {
using IceState = rtc::PeerConnection::IceState;
const char *str;
switch (state) {
case IceState::New:
str = "new";
break;
case IceState::Checking:
str = "checking";
break;
case IceState::Connected:
str = "connected";
break;
case IceState::Completed:
str = "completed";
break;
case IceState::Failed:
str = "failed";
break;
case IceState::Disconnected:
str = "disconnected";
break;
case IceState::Closed:
str = "closed";
break;
default:
str = "unknown";
break;
}
return out << str;
}

std::ostream &operator<<(std::ostream &out, rtc::PeerConnection::GatheringState state) {
using GatheringState = rtc::PeerConnection::GatheringState;
const char *str;
Expand Down
14 changes: 14 additions & 0 deletions test/capi_connectivity.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ static void sleep(unsigned int secs) { Sleep(secs * 1000); }

typedef struct {
rtcState state;
rtcIceState iceState;
rtcGatheringState gatheringState;
rtcSignalingState signalingState;
int pc;
Expand Down Expand Up @@ -53,6 +54,12 @@ static void RTC_API stateChangeCallback(int pc, rtcState state, void *ptr) {
printf("State %d: %d\n", peer == peer1 ? 1 : 2, (int)state);
}

static void RTC_API iceStateChangeCallback(int pc, rtcIceState state, void *ptr) {
Peer *peer = (Peer *)ptr;
peer->iceState = state;
printf("ICE state %d: %d\n", peer == peer1 ? 1 : 2, (int)state);
}

static void RTC_API gatheringStateCallback(int pc, rtcGatheringState state, void *ptr) {
Peer *peer = (Peer *)ptr;
peer->gatheringState = state;
Expand Down Expand Up @@ -158,6 +165,7 @@ static Peer *createPeer(const rtcConfiguration *config) {
rtcSetLocalDescriptionCallback(peer->pc, descriptionCallback);
rtcSetLocalCandidateCallback(peer->pc, candidateCallback);
rtcSetStateChangeCallback(peer->pc, stateChangeCallback);
rtcSetIceStateChangeCallback(peer->pc, iceStateChangeCallback);
rtcSetGatheringStateChangeCallback(peer->pc, gatheringStateCallback);
rtcSetSignalingStateChangeCallback(peer->pc, signalingStateCallback);

Expand Down Expand Up @@ -246,6 +254,12 @@ int test_capi_connectivity_main() {
goto error;
}

if ((peer1->iceState != RTC_ICE_CONNECTED && peer1->iceState != RTC_ICE_COMPLETED) ||
(peer2->iceState != RTC_ICE_CONNECTED && peer2->iceState != RTC_ICE_COMPLETED)) {
fprintf(stderr, "PeerConnection is not connected\n");
goto error;
}

if (!peer1->connected || !peer2->connected) {
fprintf(stderr, "DataChannel is not connected\n");
goto error;
Expand Down
16 changes: 15 additions & 1 deletion test/connectivity.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ void test_connectivity(bool signal_wrong_fingerprint) {

pc1.onStateChange([](PeerConnection::State state) { cout << "State 1: " << state << endl; });

pc1.onIceStateChange([](PeerConnection::IceState state) {
cout << "ICE state 1: " << state << endl;
});

pc1.onGatheringStateChange([](PeerConnection::GatheringState state) {
cout << "Gathering state 1: " << state << endl;
});
Expand All @@ -88,6 +92,10 @@ void test_connectivity(bool signal_wrong_fingerprint) {

pc2.onStateChange([](PeerConnection::State state) { cout << "State 2: " << state << endl; });

pc2.onIceStateChange([](PeerConnection::IceState state) {
cout << "ICE state 2: " << state << endl;
});

pc2.onGatheringStateChange([](PeerConnection::GatheringState state) {
cout << "Gathering state 2: " << state << endl;
});
Expand Down Expand Up @@ -144,10 +152,16 @@ void test_connectivity(bool signal_wrong_fingerprint) {
while ((!(adc2 = std::atomic_load(&dc2)) || !adc2->isOpen() || !dc1->isOpen()) && attempts--)
this_thread::sleep_for(1s);

if (pc1.state() != PeerConnection::State::Connected &&
if (pc1.state() != PeerConnection::State::Connected ||
pc2.state() != PeerConnection::State::Connected)
throw runtime_error("PeerConnection is not connected");

if ((pc1.iceState() != PeerConnection::IceState::Connected &&
pc1.iceState() != PeerConnection::IceState::Completed) ||
(pc2.iceState() != PeerConnection::IceState::Connected &&
pc2.iceState() != PeerConnection::IceState::Completed))
throw runtime_error("ICE is not connected");

if (!adc2 || !adc2->isOpen() || !dc1->isOpen())
throw runtime_error("DataChannel is not open");

Expand Down
2 changes: 1 addition & 1 deletion test/negotiated.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ void test_negotiated() {
while (!negotiated1->isOpen() || !negotiated2->isOpen() && attempts--)
this_thread::sleep_for(1s);

if (pc1.state() != PeerConnection::State::Connected &&
if (pc1.state() != PeerConnection::State::Connected ||
pc2.state() != PeerConnection::State::Connected)
throw runtime_error("PeerConnection is not connected");

Expand Down
2 changes: 1 addition & 1 deletion test/track.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ void test_track() {
while ((!(at2 = std::atomic_load(&t2)) || !at2->isOpen() || !t1->isOpen()) && attempts--)
this_thread::sleep_for(1s);

if (pc1.state() != PeerConnection::State::Connected &&
if (pc1.state() != PeerConnection::State::Connected ||
pc2.state() != PeerConnection::State::Connected)
throw runtime_error("PeerConnection is not connected");

Expand Down
14 changes: 13 additions & 1 deletion test/turn_connectivity.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ void test_turn_connectivity() {

pc1.onStateChange([](PeerConnection::State state) { cout << "State 1: " << state << endl; });

pc1.onIceStateChange(
[](PeerConnection::IceState state) { cout << "ICE state 1: " << state << endl; });

pc1.onGatheringStateChange([&pc1, &pc2](PeerConnection::GatheringState state) {
cout << "Gathering state 1: " << state << endl;
if (state == PeerConnection::GatheringState::Complete) {
Expand Down Expand Up @@ -69,6 +72,9 @@ void test_turn_connectivity() {

pc2.onStateChange([](PeerConnection::State state) { cout << "State 2: " << state << endl; });

pc2.onIceStateChange(
[](PeerConnection::IceState state) { cout << "ICE state 2: " << state << endl; });

pc2.onGatheringStateChange([](PeerConnection::GatheringState state) {
cout << "Gathering state 2: " << state << endl;
});
Expand Down Expand Up @@ -125,10 +131,16 @@ void test_turn_connectivity() {
while ((!(adc2 = std::atomic_load(&dc2)) || !adc2->isOpen() || !dc1->isOpen()) && attempts--)
this_thread::sleep_for(1s);

if (pc1.state() != PeerConnection::State::Connected &&
if (pc1.state() != PeerConnection::State::Connected ||
pc2.state() != PeerConnection::State::Connected)
throw runtime_error("PeerConnection is not connected");

if ((pc1.iceState() != PeerConnection::IceState::Connected &&
pc1.iceState() != PeerConnection::IceState::Completed) ||
(pc2.iceState() != PeerConnection::IceState::Connected &&
pc2.iceState() != PeerConnection::IceState::Completed))
throw runtime_error("ICE is not connected");

if (!adc2 || !adc2->isOpen() || !dc1->isOpen())
throw runtime_error("DataChannel is not open");

Expand Down

0 comments on commit 9b0f18c

Please sign in to comment.