Skip to content

Commit 1691c95

Browse files
Merge pull request #1237 from paullouisageneau/fix-renegotiationneeded
Properly implement the renegotiation needed mechanism
2 parents 9daab5b + 8c3e1b2 commit 1691c95

10 files changed

+177
-62
lines changed

DOC.md

+12
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,18 @@ Return value: the maximun length of strings copied in buffers (including the ter
371371

372372
If `local`, `remote`, or both, are `NULL`, the corresponding candidate is not copied, but the maximum length is still returned.
373373

374+
#### rtcIsNegotiationNeeded
375+
```
376+
bool rtcIsNegotiationNeeded(int pc);
377+
```
378+
379+
Return true if negotiation needs to be started or restarted, for instance to signal new tracks. If so, the user may call `rtcSetLocalDescription()` to start it.
380+
381+
Arguments:
382+
- `pc`: the Peer Connection identifier
383+
384+
Return value: true if negotiation is needed
385+
374386
#### rtcGetMaxDataChannelStream
375387
```
376388
int rtcGetMaxDataChannelStream(int pc);

include/rtc/description.hpp

+3-3
Original file line numberDiff line numberDiff line change
@@ -281,9 +281,9 @@ class RTC_CPP_EXPORT Description {
281281
int addAudio(string mid = "audio", Direction dir = Direction::SendOnly);
282282
void clearMedia();
283283

284-
variant<Media *, Application *> media(unsigned int index);
285-
variant<const Media *, const Application *> media(unsigned int index) const;
286-
unsigned int mediaCount() const;
284+
variant<Media *, Application *> media(int index);
285+
variant<const Media *, const Application *> media(int index) const;
286+
int mediaCount() const;
287287

288288
const Application *application() const;
289289
Application *application();

include/rtc/peerconnection.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ class RTC_CPP_EXPORT PeerConnection final : CheshireCat<impl::PeerConnection> {
8686
IceState iceState() const;
8787
GatheringState gatheringState() const;
8888
SignalingState signalingState() const;
89+
bool negotiationNeeded() const;
8990
bool hasMedia() const;
9091
optional<Description> localDescription() const;
9192
optional<Description> remoteDescription() const;

include/rtc/rtc.h

+2
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,8 @@ RTC_C_EXPORT int rtcGetRemoteAddress(int pc, char *buffer, int size);
227227
RTC_C_EXPORT int rtcGetSelectedCandidatePair(int pc, char *local, int localSize, char *remote,
228228
int remoteSize);
229229

230+
RTC_C_EXPORT bool rtcIsNegotiationNeeded(int pc);
231+
230232
RTC_C_EXPORT int rtcGetMaxDataChannelStream(int pc);
231233
RTC_C_EXPORT int rtcGetRemoteMaxMessageSize(int pc);
232234

pages/content/pages/reference.md

+12
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,18 @@ Return value: the maximun length of strings copied in buffers (including the ter
374374

375375
If `local`, `remote`, or both, are `NULL`, the corresponding candidate is not copied, but the maximum length is still returned.
376376

377+
#### rtcIsNegotiationNeeded
378+
```
379+
bool rtcIsNegotiationNeeded(int pc);
380+
```
381+
382+
Return true if negotiation needs to be started or restarted, for instance to signal new tracks. If so, the user may call `rtcSetLocalDescription()` to start it.
383+
384+
Arguments:
385+
- `pc`: the Peer Connection identifier
386+
387+
Return value: true if negotiation is needed
388+
377389
#### rtcGetMaxDataChannelStream
378390
```
379391
int rtcGetMaxDataChannelStream(int pc);

src/capi.cpp

+7-2
Original file line numberDiff line numberDiff line change
@@ -674,6 +674,11 @@ int rtcGetSelectedCandidatePair(int pc, char *local, int localSize, char *remote
674674
});
675675
}
676676

677+
bool rtcIsNegotiationNeeded(int pc) {
678+
return wrap([&] { return getPeerConnection(pc)->negotiationNeeded() ? 0 : 1; }) == 0 ? true
679+
: false;
680+
}
681+
677682
int rtcGetMaxDataChannelStream(int pc) {
678683
return wrap([&] {
679684
auto peerConnection = getPeerConnection(pc);
@@ -1440,7 +1445,7 @@ int rtcGetSsrcsForType(const char *mediaType, const char *sdp, uint32_t *buffer,
14401445
auto oldSDP = string(sdp);
14411446
auto description = Description(oldSDP, "unspec");
14421447
auto mediaCount = description.mediaCount();
1443-
for (unsigned int i = 0; i < mediaCount; i++) {
1448+
for (int i = 0; i < mediaCount; i++) {
14441449
if (std::holds_alternative<Description::Media *>(description.media(i))) {
14451450
auto media = std::get<Description::Media *>(description.media(i));
14461451
auto currentMediaType = lowercased(media->type());
@@ -1461,7 +1466,7 @@ int rtcSetSsrcForType(const char *mediaType, const char *sdp, char *buffer, cons
14611466
auto prevSDP = string(sdp);
14621467
auto description = Description(prevSDP, "unspec");
14631468
auto mediaCount = description.mediaCount();
1464-
for (unsigned int i = 0; i < mediaCount; i++) {
1469+
for (int i = 0; i < mediaCount; i++) {
14651470
if (std::holds_alternative<Description::Media *>(description.media(i))) {
14661471
auto media = std::get<Description::Media *>(description.media(i));
14671472
auto currentMediaType = lowercased(media->type());

src/description.cpp

+5-6
Original file line numberDiff line numberDiff line change
@@ -493,8 +493,8 @@ void Description::clearMedia() {
493493
mApplication.reset();
494494
}
495495

496-
variant<Description::Media *, Description::Application *> Description::media(unsigned int index) {
497-
if (index >= mEntries.size())
496+
variant<Description::Media *, Description::Application *> Description::media(int index) {
497+
if (index < 0 || index >= int(mEntries.size()))
498498
throw std::out_of_range("Media index out of range");
499499

500500
const auto &entry = mEntries[index];
@@ -514,9 +514,8 @@ variant<Description::Media *, Description::Application *> Description::media(uns
514514
}
515515
}
516516

517-
variant<const Description::Media *, const Description::Application *>
518-
Description::media(unsigned int index) const {
519-
if (index >= mEntries.size())
517+
variant<const Description::Media *, const Description::Application *> Description::media(int index) const {
518+
if (index < 0 || index >= int(mEntries.size()))
520519
throw std::out_of_range("Media index out of range");
521520

522521
const auto &entry = mEntries[index];
@@ -536,7 +535,7 @@ Description::media(unsigned int index) const {
536535
}
537536
}
538537

539-
unsigned int Description::mediaCount() const { return unsigned(mEntries.size()); }
538+
int Description::mediaCount() const { return int(mEntries.size()); }
540539

541540
Description::Entry::Entry(const string &mline, string mid, Direction dir)
542541
: mMid(std::move(mid)), mDirection(dir) {

src/impl/peerconnection.cpp

+97-25
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,6 @@ PeerConnection::~PeerConnection() {
8585
}
8686

8787
void PeerConnection::close() {
88-
negotiationNeeded = false;
8988
if (!closing.exchange(true)) {
9089
PLOG_VERBOSE << "Closing PeerConnection";
9190
if (auto transport = std::atomic_load(&mSctpTransport))
@@ -829,27 +828,58 @@ void PeerConnection::iterateTracks(std::function<void(shared_ptr<Track> track)>
829828
}
830829
}
831830

831+
void PeerConnection::iterateRemoteTracks(std::function<void(shared_ptr<Track> track)> func) {
832+
auto remote = remoteDescription();
833+
if(!remote)
834+
return;
835+
836+
std::vector<shared_ptr<Track>> locked;
837+
{
838+
std::shared_lock lock(mTracksMutex); // read-only
839+
locked.reserve(remote->mediaCount());
840+
for(int i = 0; i < remote->mediaCount(); ++i) {
841+
if (std::holds_alternative<Description::Media *>(remote->media(i))) {
842+
auto remoteMedia = std::get<Description::Media *>(remote->media(i));
843+
if (!remoteMedia->isRemoved())
844+
if (auto it = mTracks.find(remoteMedia->mid()); it != mTracks.end())
845+
if (auto track = it->second.lock())
846+
locked.push_back(std::move(track));
847+
}
848+
}
849+
}
850+
851+
for (auto &track : locked) {
852+
try {
853+
func(std::move(track));
854+
} catch (const std::exception &e) {
855+
PLOG_WARNING << e.what();
856+
}
857+
}
858+
}
859+
860+
832861
void PeerConnection::openTracks() {
833862
#if RTC_ENABLE_MEDIA
834-
if (auto transport = std::atomic_load(&mDtlsTransport)) {
835-
auto srtpTransport = std::dynamic_pointer_cast<DtlsSrtpTransport>(transport);
836-
837-
iterateTracks([&](const shared_ptr<Track> &track) {
838-
if (!track->isOpen()) {
839-
if (srtpTransport) {
840-
track->open(srtpTransport);
841-
} else {
842-
// A track was added during a latter renegotiation, whereas SRTP transport was
843-
// not initialized. This is an optimization to use the library with data
844-
// channels only. Set forceMediaTransport to true to initialize the transport
845-
// before dynamically adding tracks.
846-
auto errorMsg = "The connection has no media transport";
847-
PLOG_ERROR << errorMsg;
848-
track->triggerError(errorMsg);
849-
}
863+
auto transport = std::atomic_load(&mDtlsTransport);
864+
if (!transport)
865+
return;
866+
867+
auto srtpTransport = std::dynamic_pointer_cast<DtlsSrtpTransport>(transport);
868+
iterateRemoteTracks([&](shared_ptr<Track> track) {
869+
if(!track->isOpen()) {
870+
if (srtpTransport) {
871+
track->open(srtpTransport);
872+
} else {
873+
// A track was added during a latter renegotiation, whereas SRTP transport was
874+
// not initialized. This is an optimization to use the library with data
875+
// channels only. Set forceMediaTransport to true to initialize the transport
876+
// before dynamically adding tracks.
877+
auto errorMsg = "The connection has no media transport";
878+
PLOG_ERROR << errorMsg;
879+
track->triggerError(errorMsg);
850880
}
851-
});
852-
}
881+
}
882+
});
853883
#endif
854884
}
855885

@@ -872,7 +902,7 @@ void PeerConnection::validateRemoteDescription(const Description &description) {
872902
throw std::invalid_argument("Remote description has no media line");
873903

874904
int activeMediaCount = 0;
875-
for (unsigned int i = 0; i < description.mediaCount(); ++i)
905+
for (int i = 0; i < description.mediaCount(); ++i)
876906
std::visit(rtc::overloaded{[&](const Description::Application *application) {
877907
if (!application->isRemoved())
878908
++activeMediaCount;
@@ -900,7 +930,7 @@ void PeerConnection::processLocalDescription(Description description) {
900930

901931
if (auto remote = remoteDescription()) {
902932
// Reciprocate remote description
903-
for (unsigned int i = 0; i < remote->mediaCount(); ++i)
933+
for (int i = 0; i < remote->mediaCount(); ++i)
904934
std::visit( // reciprocate each media
905935
rtc::overloaded{
906936
[&](Description::Application *remoteApp) {
@@ -1027,8 +1057,7 @@ void PeerConnection::processLocalDescription(Description description) {
10271057
}
10281058
}
10291059

1030-
// There might be no media at this point if the user created a Track, deleted it,
1031-
// then called setLocalDescription().
1060+
// There might be no media at this point, for instance if the user deleted tracks
10321061
if (description.mediaCount() == 0)
10331062
throw std::runtime_error("No DataChannel or Track to negotiate");
10341063
}
@@ -1102,15 +1131,19 @@ void PeerConnection::processRemoteDescription(Description description) {
11021131
mRemoteDescription->addCandidates(std::move(existingCandidates));
11031132
}
11041133

1134+
auto dtlsTransport = std::atomic_load(&mDtlsTransport);
11051135
if (description.hasApplication()) {
1106-
auto dtlsTransport = std::atomic_load(&mDtlsTransport);
11071136
auto sctpTransport = std::atomic_load(&mSctpTransport);
11081137
if (!sctpTransport && dtlsTransport &&
11091138
dtlsTransport->state() == Transport::State::Connected)
11101139
initSctpTransport();
11111140
} else {
11121141
mProcessor.enqueue(&PeerConnection::remoteCloseDataChannels, shared_from_this());
11131142
}
1143+
1144+
if (dtlsTransport && dtlsTransport->state() == Transport::State::Connected)
1145+
mProcessor.enqueue(&PeerConnection::openTracks, shared_from_this());
1146+
11141147
}
11151148

11161149
void PeerConnection::processRemoteCandidate(Candidate candidate) {
@@ -1156,6 +1189,45 @@ string PeerConnection::localBundleMid() const {
11561189
return mLocalDescription ? mLocalDescription->bundleMid() : "0";
11571190
}
11581191

1192+
bool PeerConnection::negotiationNeeded() const {
1193+
auto description = localDescription();
1194+
1195+
{
1196+
std::shared_lock lock(mDataChannelsMutex);
1197+
if (!mDataChannels.empty() || !mUnassignedDataChannels.empty())
1198+
if(!description || !description->hasApplication()) {
1199+
PLOG_DEBUG << "Negotiation needed for data channels";
1200+
return true;
1201+
}
1202+
}
1203+
1204+
{
1205+
std::shared_lock lock(mTracksMutex);
1206+
for(const auto &[mid, weakTrack] : mTracks)
1207+
if (auto track = weakTrack.lock())
1208+
if (!description || !description->hasMid(track->mid())) {
1209+
PLOG_DEBUG << "Negotiation needed to add track, mid=" << track->mid();
1210+
return true;
1211+
}
1212+
1213+
if(description) {
1214+
for(int i = 0; i < description->mediaCount(); ++i) {
1215+
if (std::holds_alternative<Description::Media *>(description->media(i))) {
1216+
auto media = std::get<Description::Media *>(description->media(i));
1217+
if (!media->isRemoved())
1218+
if (auto it = mTracks.find(media->mid()); it != mTracks.end())
1219+
if (auto track = it->second.lock(); !track || track->isClosed()) {
1220+
PLOG_DEBUG << "Negotiation needed to remove track, mid=" << track->mid();
1221+
return true;
1222+
}
1223+
}
1224+
}
1225+
}
1226+
}
1227+
1228+
return false;
1229+
}
1230+
11591231
void PeerConnection::setMediaHandler(shared_ptr<MediaHandler> handler) {
11601232
std::unique_lock lock(mMediaHandlerMutex);
11611233
mMediaHandler = handler;
@@ -1321,7 +1393,7 @@ void PeerConnection::updateTrackSsrcCache(const Description &description) {
13211393
std::unique_lock lock(mTracksMutex); // for safely writing to mTracksBySsrc
13221394

13231395
// Setup SSRC -> Track mapping
1324-
for (unsigned int i = 0; i < description.mediaCount(); ++i)
1396+
for (int i = 0; i < description.mediaCount(); ++i)
13251397
std::visit( // ssrc -> track mapping
13261398
rtc::overloaded{
13271399
[&](Description::Application const *) { return; },

src/impl/peerconnection.hpp

+5-3
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ struct PeerConnection : std::enable_shared_from_this<PeerConnection> {
7070

7171
shared_ptr<Track> emplaceTrack(Description::Media description);
7272
void iterateTracks(std::function<void(shared_ptr<Track> track)> func);
73+
void iterateRemoteTracks(std::function<void(shared_ptr<Track> track)> func);
7374
void openTracks();
7475
void closeTracks();
7576

@@ -80,6 +81,8 @@ struct PeerConnection : std::enable_shared_from_this<PeerConnection> {
8081
void processRemoteCandidate(Candidate candidate);
8182
string localBundleMid() const;
8283

84+
bool negotiationNeeded() const;
85+
8386
void setMediaHandler(shared_ptr<MediaHandler> handler);
8487
shared_ptr<MediaHandler> getMediaHandler();
8588

@@ -115,7 +118,6 @@ struct PeerConnection : std::enable_shared_from_this<PeerConnection> {
115118
std::atomic<IceState> iceState = IceState::New;
116119
std::atomic<GatheringState> gatheringState = GatheringState::New;
117120
std::atomic<SignalingState> signalingState = SignalingState::Stable;
118-
std::atomic<bool> negotiationNeeded = false;
119121
std::atomic<bool> closing = false;
120122
std::mutex signalingMutex;
121123

@@ -154,12 +156,12 @@ struct PeerConnection : std::enable_shared_from_this<PeerConnection> {
154156

155157
std::unordered_map<uint16_t, weak_ptr<DataChannel>> mDataChannels; // by stream ID
156158
std::vector<weak_ptr<DataChannel>> mUnassignedDataChannels;
157-
std::shared_mutex mDataChannelsMutex;
159+
mutable std::shared_mutex mDataChannelsMutex;
158160

159161
std::unordered_map<string, weak_ptr<Track>> mTracks; // by mid
160162
std::unordered_map<uint32_t, weak_ptr<Track>> mTracksBySsrc; // by SSRC
161163
std::vector<weak_ptr<Track>> mTrackLines; // by SDP order
162-
std::shared_mutex mTracksMutex;
164+
mutable std::shared_mutex mTracksMutex;
163165

164166
Queue<shared_ptr<DataChannel>> mPendingDataChannels;
165167
Queue<shared_ptr<Track>> mPendingTracks;

0 commit comments

Comments
 (0)