mirror of
https://github.com/mii443/libdatachannel.git
synced 2025-08-22 15:15:28 +00:00
Added signaling state to PeerConnection
This commit is contained in:
@ -34,8 +34,8 @@ namespace rtc {
|
||||
|
||||
class Description {
|
||||
public:
|
||||
enum class Type { Unspec = 0, Offer = 1, Answer = 2 };
|
||||
enum class Role { ActPass = 0, Passive = 1, Active = 2 };
|
||||
enum class Type { Unspec, Offer, Answer, Pranswer, Rollback };
|
||||
enum class Role { ActPass, Passive, Active };
|
||||
enum class Direction { SendOnly, RecvOnly, SendRecv, Inactive, Unknown };
|
||||
|
||||
Description(const string &sdp, const string &typeString = "");
|
||||
@ -45,7 +45,6 @@ public:
|
||||
Type type() const;
|
||||
string typeString() const;
|
||||
Role role() const;
|
||||
string roleString() const;
|
||||
string bundleMid() const;
|
||||
string iceUfrag() const;
|
||||
string icePwd() const;
|
||||
@ -193,6 +192,9 @@ public:
|
||||
|
||||
Application *application();
|
||||
|
||||
static Type stringToType(const string &typeString);
|
||||
static string typeToString(Type type);
|
||||
|
||||
private:
|
||||
std::optional<Candidate> defaultCandidate() const;
|
||||
std::shared_ptr<Entry> createEntry(string mline, string mid, Direction dir);
|
||||
@ -214,14 +216,12 @@ private:
|
||||
// Candidates
|
||||
std::vector<Candidate> mCandidates;
|
||||
bool mEnded = false;
|
||||
|
||||
static Type stringToType(const string &typeString);
|
||||
static string typeToString(Type type);
|
||||
static string roleToString(Role role);
|
||||
};
|
||||
|
||||
} // namespace rtc
|
||||
|
||||
std::ostream &operator<<(std::ostream &out, const rtc::Description &description);
|
||||
std::ostream &operator<<(std::ostream &out, rtc::Description::Type type);
|
||||
std::ostream &operator<<(std::ostream &out, rtc::Description::Role role);
|
||||
|
||||
#endif
|
||||
|
@ -67,6 +67,14 @@ public:
|
||||
Complete = RTC_GATHERING_COMPLETE
|
||||
};
|
||||
|
||||
enum class SignalingState : int {
|
||||
Stable = RTC_SIGNALING_STABLE,
|
||||
HaveLocalOffer = RTC_SIGNALING_HAVE_LOCAL_OFFER,
|
||||
HaveRemoteOffer = RTC_SIGNALING_HAVE_REMOTE_OFFER,
|
||||
HaveLocalPranswer = RTC_SIGNALING_HAVE_LOCAL_PRANSWER,
|
||||
HaveRemotePranswer = RTC_SIGNALING_HAVE_REMOTE_PRANSWER,
|
||||
} rtcSignalingState;
|
||||
|
||||
PeerConnection(void);
|
||||
PeerConnection(const Configuration &config);
|
||||
~PeerConnection();
|
||||
@ -76,6 +84,7 @@ public:
|
||||
const Configuration *config() const;
|
||||
State state() const;
|
||||
GatheringState gatheringState() const;
|
||||
SignalingState signalingState() const;
|
||||
bool hasLocalDescription() const;
|
||||
bool hasRemoteDescription() const;
|
||||
bool hasMedia() const;
|
||||
@ -83,8 +92,9 @@ public:
|
||||
std::optional<Description> remoteDescription() const;
|
||||
std::optional<string> localAddress() const;
|
||||
std::optional<string> remoteAddress() const;
|
||||
bool getSelectedCandidatePair(Candidate *local, Candidate *remote);
|
||||
|
||||
void setLocalDescription();
|
||||
void setLocalDescription(Description::Type type = Description::Type::Unspec);
|
||||
void setRemoteDescription(Description description);
|
||||
void addRemoteCandidate(Candidate candidate);
|
||||
|
||||
@ -100,6 +110,7 @@ public:
|
||||
void onLocalCandidate(std::function<void(Candidate candidate)> callback);
|
||||
void onStateChange(std::function<void(State state)> callback);
|
||||
void onGatheringStateChange(std::function<void(GatheringState state)> callback);
|
||||
void onSignalingStateChange(std::function<void(SignalingState state)> callback);
|
||||
|
||||
// Stats
|
||||
void clearStats();
|
||||
@ -111,9 +122,6 @@ public:
|
||||
std::shared_ptr<Track> addTrack(Description::Media description);
|
||||
void onTrack(std::function<void(std::shared_ptr<Track> track)> callback);
|
||||
|
||||
// libnice only
|
||||
bool getSelectedCandidatePair(Candidate *local, Candidate *remote);
|
||||
|
||||
private:
|
||||
std::shared_ptr<IceTransport> initIceTransport(Description::Role role);
|
||||
std::shared_ptr<DtlsTransport> initDtlsTransport();
|
||||
@ -143,6 +151,7 @@ private:
|
||||
void triggerTrack(std::shared_ptr<Track> track);
|
||||
bool changeState(State state);
|
||||
bool changeGatheringState(GatheringState state);
|
||||
bool changeSignalingState(SignalingState state);
|
||||
|
||||
void resetCallbacks();
|
||||
|
||||
@ -168,6 +177,7 @@ private:
|
||||
|
||||
std::atomic<State> mState;
|
||||
std::atomic<GatheringState> mGatheringState;
|
||||
std::atomic<SignalingState> mSignalingState;
|
||||
std::atomic<bool> mNegociationNeeded;
|
||||
|
||||
synchronized_callback<std::shared_ptr<DataChannel>> mDataChannelCallback;
|
||||
@ -175,12 +185,14 @@ private:
|
||||
synchronized_callback<Candidate> mLocalCandidateCallback;
|
||||
synchronized_callback<State> mStateChangeCallback;
|
||||
synchronized_callback<GatheringState> mGatheringStateChangeCallback;
|
||||
synchronized_callback<SignalingState> mSignalingStateChangeCallback;
|
||||
synchronized_callback<std::shared_ptr<Track>> mTrackCallback;
|
||||
};
|
||||
|
||||
} // namespace rtc
|
||||
|
||||
std::ostream &operator<<(std::ostream &out, const rtc::PeerConnection::State &state);
|
||||
std::ostream &operator<<(std::ostream &out, const rtc::PeerConnection::GatheringState &state);
|
||||
std::ostream &operator<<(std::ostream &out, rtc::PeerConnection::State state);
|
||||
std::ostream &operator<<(std::ostream &out, rtc::PeerConnection::GatheringState state);
|
||||
std::ostream &operator<<(std::ostream &out, rtc::PeerConnection::SignalingState state);
|
||||
|
||||
#endif
|
||||
|
@ -59,6 +59,14 @@ typedef enum {
|
||||
RTC_GATHERING_COMPLETE = 2
|
||||
} rtcGatheringState;
|
||||
|
||||
typedef enum {
|
||||
RTC_SIGNALING_STABLE = 0,
|
||||
RTC_SIGNALING_HAVE_LOCAL_OFFER = 1,
|
||||
RTC_SIGNALING_HAVE_REMOTE_OFFER = 2,
|
||||
RTC_SIGNALING_HAVE_LOCAL_PRANSWER = 3,
|
||||
RTC_SIGNALING_HAVE_REMOTE_PRANSWER = 4,
|
||||
} rtcSignalingState;
|
||||
|
||||
typedef enum { // Don't change, it must match plog severity
|
||||
RTC_LOG_NONE = 0,
|
||||
RTC_LOG_FATAL = 1,
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
#include <sstream>
|
||||
#include <unordered_map>
|
||||
|
||||
using std::shared_ptr;
|
||||
using std::size_t;
|
||||
@ -767,12 +768,14 @@ Description::Video::Video(string mid, Direction dir)
|
||||
: Media("video 9 UDP/TLS/RTP/SAVPF", std::move(mid), dir) {}
|
||||
|
||||
Description::Type Description::stringToType(const string &typeString) {
|
||||
if (typeString == "offer")
|
||||
return Type::Offer;
|
||||
else if (typeString == "answer")
|
||||
return Type::Answer;
|
||||
else
|
||||
return Type::Unspec;
|
||||
using TypeMap_t = std::unordered_map<string, Type>;
|
||||
static const TypeMap_t TypeMap = {{"unspec", Type::Unspec},
|
||||
{"offer", Type::Offer},
|
||||
{"answer", Type::Pranswer},
|
||||
{"pranswer", Type::Pranswer},
|
||||
{"rollback", Type::Rollback}};
|
||||
auto it = TypeMap.find(typeString);
|
||||
return it != TypeMap.end() ? it->second : Type::Unspec;
|
||||
}
|
||||
|
||||
string Description::typeToString(Type type) {
|
||||
@ -781,19 +784,12 @@ string Description::typeToString(Type type) {
|
||||
return "offer";
|
||||
case Type::Answer:
|
||||
return "answer";
|
||||
case Type::Pranswer:
|
||||
return "pranswer";
|
||||
case Type::Rollback:
|
||||
return "rollback";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
string Description::roleToString(Role role) {
|
||||
switch (role) {
|
||||
case Role::Active:
|
||||
return "active";
|
||||
case Role::Passive:
|
||||
return "passive";
|
||||
default:
|
||||
return "actpass";
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
@ -802,3 +798,25 @@ string Description::roleToString(Role role) {
|
||||
std::ostream &operator<<(std::ostream &out, const rtc::Description &description) {
|
||||
return out << std::string(description);
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &out, rtc::Description::Type type) {
|
||||
return out << rtc::Description::typeToString(type);
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &out, rtc::Description::Role role) {
|
||||
using Role = rtc::Description::Role;
|
||||
const char *str;
|
||||
switch (role) {
|
||||
case Role::Active:
|
||||
str = "active";
|
||||
break;
|
||||
case Role::Passive:
|
||||
str = "passive";
|
||||
break;
|
||||
default:
|
||||
str = "actpass";
|
||||
break;
|
||||
}
|
||||
return out << str;
|
||||
}
|
||||
|
||||
|
@ -44,7 +44,8 @@ PeerConnection::PeerConnection() : PeerConnection(Configuration()) {}
|
||||
|
||||
PeerConnection::PeerConnection(const Configuration &config)
|
||||
: mConfig(config), mCertificate(make_certificate()), mProcessor(std::make_unique<Processor>()),
|
||||
mState(State::New), mGatheringState(GatheringState::New), mNegociationNeeded(false) {
|
||||
mState(State::New), mGatheringState(GatheringState::New),
|
||||
mSignalingState(SignalingState::Stable), mNegociationNeeded(false) {
|
||||
PLOG_VERBOSE << "Creating PeerConnection";
|
||||
|
||||
if (config.portRangeEnd && config.portRangeBegin > config.portRangeEnd)
|
||||
@ -74,6 +75,8 @@ PeerConnection::State PeerConnection::state() const { return mState; }
|
||||
|
||||
PeerConnection::GatheringState PeerConnection::gatheringState() const { return mGatheringState; }
|
||||
|
||||
PeerConnection::SignalingState PeerConnection::signalingState() const { return mSignalingState; }
|
||||
|
||||
std::optional<Description> PeerConnection::localDescription() const {
|
||||
std::lock_guard lock(mLocalDescriptionMutex);
|
||||
return mLocalDescription;
|
||||
@ -99,7 +102,7 @@ bool PeerConnection::hasMedia() const {
|
||||
return local && local->hasAudioOrVideo();
|
||||
}
|
||||
|
||||
void PeerConnection::setLocalDescription() {
|
||||
void PeerConnection::setLocalDescription(Description::Type type) {
|
||||
PLOG_VERBOSE << "Setting local description";
|
||||
|
||||
if (!mNegociationNeeded.exchange(false)) {
|
||||
@ -107,13 +110,30 @@ void PeerConnection::setLocalDescription() {
|
||||
return;
|
||||
}
|
||||
|
||||
// RFC 5763: The endpoint that is the offerer MUST use the setup attribute value of
|
||||
// setup:actpass.
|
||||
// See https://tools.ietf.org/html/rfc5763#section-5
|
||||
auto iceTransport = initIceTransport(Description::Role::ActPass);
|
||||
Description localDescription = iceTransport->getLocalDescription(Description::Type::Offer);
|
||||
// Guess the description type if unspecified
|
||||
if (type == Description::Type::Unspec) {
|
||||
if (mSignalingState == SignalingState::HaveRemoteOffer)
|
||||
type = Description::Type::Answer;
|
||||
else
|
||||
type = Description::Type::Offer;
|
||||
}
|
||||
|
||||
auto iceTransport = std::atomic_load(&mIceTransport);
|
||||
if (!iceTransport) {
|
||||
if (type != Description::Type::Offer) {
|
||||
// RFC 5763: The endpoint that is the offerer MUST use the setup attribute value of
|
||||
// setup:actpass.
|
||||
// See https://tools.ietf.org/html/rfc5763#section-5
|
||||
if (!iceTransport)
|
||||
iceTransport = initIceTransport(Description::Role::ActPass);
|
||||
}
|
||||
}
|
||||
|
||||
Description localDescription = iceTransport->getLocalDescription(type);
|
||||
processLocalDescription(localDescription);
|
||||
iceTransport->gatherLocalCandidates();
|
||||
|
||||
if (mGatheringState == GatheringState::New)
|
||||
iceTransport->gatherLocalCandidates();
|
||||
}
|
||||
|
||||
void PeerConnection::setRemoteDescription(Description description) {
|
||||
@ -143,21 +163,40 @@ void PeerConnection::setRemoteDescription(Description description) {
|
||||
throw std::logic_error("Got the local description as remote description");
|
||||
}
|
||||
|
||||
description.hintType(hasLocalDescription() ? Description::Type::Answer
|
||||
: Description::Type::Offer);
|
||||
|
||||
// If there is no remote description, this is the first negociation
|
||||
// Check it is what we expect
|
||||
if (!hasRemoteDescription()) {
|
||||
if (description.type() == Description::Type::Offer) {
|
||||
if (hasLocalDescription()) {
|
||||
PLOG_ERROR << "Got a remote offer description while an answer was expected";
|
||||
throw std::logic_error("Got an unexpected remote offer description");
|
||||
}
|
||||
} else { // Answer
|
||||
PLOG_ERROR << "Got a remote answer description while an offer was expected";
|
||||
throw std::logic_error("Got an unexpected remote answer description");
|
||||
// Get the new signaling state
|
||||
SignalingState signalingState = mSignalingState.load();
|
||||
SignalingState newSignalingState;
|
||||
switch (signalingState) {
|
||||
case SignalingState::Stable:
|
||||
description.hintType(Description::Type::Offer);
|
||||
if (description.type() != Description::Type::Offer) {
|
||||
LOG_ERROR << "Unexpected remote " << description.type()
|
||||
<< " description in signaling state " << signalingState << ", expected offer";
|
||||
std::ostringstream oss;
|
||||
oss << "Unexpected remote " << description.type() << " description";
|
||||
throw std::logic_error(oss.str());
|
||||
}
|
||||
newSignalingState = SignalingState::HaveRemoteOffer;
|
||||
break;
|
||||
|
||||
case SignalingState::HaveLocalOffer:
|
||||
case SignalingState::HaveRemotePranswer:
|
||||
description.hintType(Description::Type::Answer);
|
||||
if (description.type() != Description::Type::Answer ||
|
||||
description.type() != Description::Type::Pranswer) {
|
||||
LOG_ERROR << "Unexpected remote " << description.type()
|
||||
<< " description in signaling state " << signalingState
|
||||
<< ", expected answer";
|
||||
std::ostringstream oss;
|
||||
oss << "Unexpected remote " << description.type() << " description";
|
||||
throw std::logic_error(oss.str());
|
||||
}
|
||||
newSignalingState = SignalingState::Stable;
|
||||
break;
|
||||
|
||||
default:
|
||||
LOG_ERROR << "Unexpected remote description in signaling state " << signalingState;
|
||||
throw std::logic_error("Unexpected remote description");
|
||||
}
|
||||
|
||||
// Candidates will be added at the end, extract them for now
|
||||
@ -168,12 +207,19 @@ void PeerConnection::setRemoteDescription(Description description) {
|
||||
iceTransport = initIceTransport(Description::Role::ActPass);
|
||||
iceTransport->setRemoteDescription(description);
|
||||
|
||||
if (description.hasApplication()) {
|
||||
if (auto current = remoteDescription(); current && !current->hasApplication())
|
||||
if (auto dtlsTransport = std::atomic_load(&mDtlsTransport);
|
||||
dtlsTransport && dtlsTransport->state() == Transport::State::Connected)
|
||||
initSctpTransport();
|
||||
}
|
||||
|
||||
{
|
||||
// Set as remote description
|
||||
std::lock_guard lock(mRemoteDescriptionMutex);
|
||||
|
||||
|
||||
std::vector<Candidate> existingCandidates;
|
||||
if(mRemoteDescription)
|
||||
if (mRemoteDescription)
|
||||
existingCandidates = mRemoteDescription->extractCandidates();
|
||||
|
||||
mRemoteDescription.emplace(std::move(description));
|
||||
@ -181,11 +227,12 @@ void PeerConnection::setRemoteDescription(Description description) {
|
||||
mRemoteDescription->addCandidate(candidate);
|
||||
}
|
||||
|
||||
changeSignalingState(newSignalingState);
|
||||
|
||||
if (description.type() == Description::Type::Offer) {
|
||||
// This is an offer, we need to answer
|
||||
Description localDescription = iceTransport->getLocalDescription(Description::Type::Answer);
|
||||
processLocalDescription(localDescription);
|
||||
iceTransport->gatherLocalCandidates();
|
||||
mNegociationNeeded = true;
|
||||
setLocalDescription(Description::Type::Answer);
|
||||
} else {
|
||||
// This is an answer
|
||||
auto sctpTransport = std::atomic_load(&mSctpTransport);
|
||||
@ -204,6 +251,8 @@ void PeerConnection::setRemoteDescription(Description description) {
|
||||
}
|
||||
std::swap(mDataChannels, newDataChannels);
|
||||
}
|
||||
|
||||
changeSignalingState(SignalingState::Stable);
|
||||
}
|
||||
|
||||
for (const auto &candidate : remoteCandidates)
|
||||
@ -261,7 +310,7 @@ shared_ptr<DataChannel> PeerConnection::addDataChannel(string label, string prot
|
||||
if (transport->state() == SctpTransport::State::Connected)
|
||||
channel->open(transport);
|
||||
|
||||
// Renegociation is needed if the current local description does not have application
|
||||
// Renegociation is needed iff the current local description does not have application
|
||||
std::lock_guard lock(mLocalDescriptionMutex);
|
||||
if (!mLocalDescription || !mLocalDescription->hasApplication())
|
||||
mNegociationNeeded = true;
|
||||
@ -297,6 +346,10 @@ void PeerConnection::onGatheringStateChange(std::function<void(GatheringState st
|
||||
mGatheringStateChangeCallback = callback;
|
||||
}
|
||||
|
||||
void PeerConnection::onSignalingStateChange(std::function<void(SignalingState state)> callback) {
|
||||
mSignalingStateChangeCallback = callback;
|
||||
}
|
||||
|
||||
std::shared_ptr<Track> PeerConnection::addTrack(Description::Media description) {
|
||||
if (hasLocalDescription())
|
||||
throw std::logic_error("Tracks must be created before local description");
|
||||
@ -401,7 +454,7 @@ shared_ptr<DtlsTransport> PeerConnection::initDtlsTransport() {
|
||||
|
||||
switch (state) {
|
||||
case DtlsTransport::State::Connected:
|
||||
if (auto local = localDescription(); local && local->hasApplication())
|
||||
if (auto remote = remoteDescription(); remote && remote->hasApplication())
|
||||
initSctpTransport();
|
||||
else
|
||||
changeState(State::Connected);
|
||||
@ -833,9 +886,9 @@ void PeerConnection::processLocalDescription(Description description) {
|
||||
{
|
||||
// Set as local description
|
||||
std::lock_guard lock(mLocalDescriptionMutex);
|
||||
|
||||
|
||||
std::vector<Candidate> existingCandidates;
|
||||
if(mLocalDescription)
|
||||
if (mLocalDescription)
|
||||
existingCandidates = mLocalDescription->extractCandidates();
|
||||
|
||||
mLocalDescription.emplace(std::move(description));
|
||||
@ -910,6 +963,16 @@ bool PeerConnection::changeGatheringState(GatheringState state) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PeerConnection::changeSignalingState(SignalingState state) {
|
||||
if (mSignalingState.exchange(state) != state) {
|
||||
std::ostringstream s;
|
||||
s << state;
|
||||
PLOG_INFO << "Changed signaling state to " << s.str();
|
||||
mProcessor->enqueue([this, state] { mSignalingStateChangeCallback(state); });
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void PeerConnection::resetCallbacks() {
|
||||
// Unregister all callbacks
|
||||
mDataChannelCallback = nullptr;
|
||||
@ -957,7 +1020,7 @@ std::optional<std::chrono::milliseconds> PeerConnection::rtt() {
|
||||
|
||||
std::ostream &operator<<(std::ostream &out, const rtc::PeerConnection::State &state) {
|
||||
using State = rtc::PeerConnection::State;
|
||||
std::string str;
|
||||
const char *str;
|
||||
switch (state) {
|
||||
case State::New:
|
||||
str = "new";
|
||||
@ -986,13 +1049,13 @@ std::ostream &operator<<(std::ostream &out, const rtc::PeerConnection::State &st
|
||||
|
||||
std::ostream &operator<<(std::ostream &out, const rtc::PeerConnection::GatheringState &state) {
|
||||
using GatheringState = rtc::PeerConnection::GatheringState;
|
||||
std::string str;
|
||||
const char *str;
|
||||
switch (state) {
|
||||
case GatheringState::New:
|
||||
str = "new";
|
||||
break;
|
||||
case GatheringState::InProgress:
|
||||
str = "in_progress";
|
||||
str = "in-progress";
|
||||
break;
|
||||
case GatheringState::Complete:
|
||||
str = "complete";
|
||||
@ -1003,3 +1066,29 @@ std::ostream &operator<<(std::ostream &out, const rtc::PeerConnection::Gathering
|
||||
}
|
||||
return out << str;
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &out, const rtc::PeerConnection::SignalingState &state) {
|
||||
using SignalingState = rtc::PeerConnection::SignalingState;
|
||||
const char *str;
|
||||
switch (state) {
|
||||
case SignalingState::Stable:
|
||||
str = "stable";
|
||||
break;
|
||||
case SignalingState::HaveLocalOffer:
|
||||
str = "have-local-offer";
|
||||
break;
|
||||
case SignalingState::HaveRemoteOffer:
|
||||
str = "have-remote-offer";
|
||||
break;
|
||||
case SignalingState::HaveLocalPranswer:
|
||||
str = "have-local-pranswer";
|
||||
break;
|
||||
case SignalingState::HaveRemotePranswer:
|
||||
str = "have-remote-pranswer";
|
||||
break;
|
||||
default:
|
||||
str = "unknown";
|
||||
break;
|
||||
}
|
||||
return out << str;
|
||||
}
|
||||
|
Reference in New Issue
Block a user