Browse Source

Introduce createOffer() and createAnswer() methods

Paul-Louis Ageneau 6 months ago
parent
commit
00ffc07077

+ 2 - 1
include/rtc/description.hpp

@@ -66,9 +66,10 @@ public:
 	bool ended() const;
 
 	void hintType(Type type);
-	void setFingerprint(CertificateFingerprint f);
 	void addIceOption(string option);
 	void removeIceOption(const string &option);
+	void setIceAttribute(string ufrag, string pwd);
+	void setFingerprint(CertificateFingerprint f);
 
 	std::vector<string> attributes() const;
 	void addAttribute(string attr);

+ 5 - 1
include/rtc/peerconnection.hpp

@@ -97,9 +97,13 @@ public:
 	bool getSelectedCandidatePair(Candidate *local, Candidate *remote);
 
 	void setLocalDescription(Description::Type type = Description::Type::Unspec, LocalDescriptionInit init = {});
+	void gatherLocalCandidates(std::vector<IceServer> additionalIceServers = {});
 	void setRemoteDescription(Description description);
 	void addRemoteCandidate(Candidate candidate);
-	void gatherLocalCandidates(std::vector<IceServer> additionalIceServers = {});
+
+	// For specific use cases only
+	Description createOffer();
+	Description createAnswer();
 
 	void setMediaHandler(shared_ptr<MediaHandler> handler);
 	shared_ptr<MediaHandler> getMediaHandler();

+ 15 - 10
src/description.cpp

@@ -216,6 +216,21 @@ void Description::hintType(Type type) {
 		mType = type;
 }
 
+void Description::addIceOption(string option) {
+	if (std::find(mIceOptions.begin(), mIceOptions.end(), option) == mIceOptions.end())
+		mIceOptions.emplace_back(std::move(option));
+}
+
+void Description::removeIceOption(const string &option) {
+	mIceOptions.erase(std::remove(mIceOptions.begin(), mIceOptions.end(), option),
+	                  mIceOptions.end());
+}
+
+void Description::setIceAttribute(string ufrag, string pwd) {
+	mIceUfrag = std::move(ufrag);
+	mIcePwd = std::move(pwd);
+}
+
 void Description::setFingerprint(CertificateFingerprint f) {
 	if (!f.isValid())
 		throw std::invalid_argument("Invalid " +
@@ -227,16 +242,6 @@ void Description::setFingerprint(CertificateFingerprint f) {
 	mFingerprint = std::move(f);
 }
 
-void Description::addIceOption(string option) {
-	if (std::find(mIceOptions.begin(), mIceOptions.end(), option) == mIceOptions.end())
-		mIceOptions.emplace_back(std::move(option));
-}
-
-void Description::removeIceOption(const string &option) {
-	mIceOptions.erase(std::remove(mIceOptions.begin(), mIceOptions.end(), option),
-	                  mIceOptions.end());
-}
-
 std::vector<string> Description::Entry::attributes() const { return mAttributes; }
 
 void Description::Entry::addAttribute(string attr) {

+ 67 - 74
src/impl/peerconnection.cpp

@@ -925,7 +925,7 @@ void PeerConnection::validateRemoteDescription(const Description &description) {
 	PLOG_VERBOSE << "Remote description looks valid";
 }
 
-void PeerConnection::processLocalDescription(Description description) {
+void PeerConnection::populateLocalDescription(Description &description) const {
 	const uint16_t localSctpPort = DEFAULT_SCTP_PORT;
 	const size_t localMaxMessageSize =
 	    config.maxMessageSize.value_or(DEFAULT_LOCAL_MAX_MESSAGE_SIZE);
@@ -935,7 +935,7 @@ void PeerConnection::processLocalDescription(Description description) {
 
 	if (auto remote = remoteDescription()) {
 		// Reciprocate remote description
-		for (int i = 0; i < remote->mediaCount(); ++i)
+		for (int i = 0; i < remote->mediaCount(); ++i) {
 			std::visit( // reciprocate each media
 			    rtc::overloaded{
 			        [&](Description::Application *remoteApp) {
@@ -950,78 +950,46 @@ void PeerConnection::processLocalDescription(Description description) {
 					                   << app.mid() << "\"";
 
 					        description.addMedia(std::move(app));
-					        return;
-				        }
 
-				        auto reciprocated = remoteApp->reciprocate();
-				        reciprocated.hintSctpPort(localSctpPort);
-				        reciprocated.setMaxMessageSize(localMaxMessageSize);
+				        } else {
+							auto reciprocated = remoteApp->reciprocate();
+							reciprocated.hintSctpPort(localSctpPort);
+							reciprocated.setMaxMessageSize(localMaxMessageSize);
 
-				        PLOG_DEBUG << "Reciprocating application in local description, mid=\""
-				                   << reciprocated.mid() << "\"";
+							PLOG_DEBUG << "Reciprocating application in local description, mid=\""
+								       << reciprocated.mid() << "\"";
 
-				        description.addMedia(std::move(reciprocated));
+							description.addMedia(std::move(reciprocated));
+						}
 			        },
 			        [&](Description::Media *remoteMedia) {
-				        std::unique_lock lock(mTracksMutex); // we may emplace a track
-				        if (auto it = mTracks.find(remoteMedia->mid()); it != mTracks.end()) {
-					        // Prefer local description
-					        if (auto track = it->second.lock()) {
-						        auto media = track->description();
-
-						        PLOG_DEBUG << "Adding media to local description, mid=\""
-						                   << media.mid() << "\", removed=" << std::boolalpha
-						                   << media.isRemoved();
-
-						        description.addMedia(std::move(media));
-
-					        } else {
-						        auto reciprocated = remoteMedia->reciprocate();
-						        reciprocated.markRemoved();
+				        std::shared_lock lock(mTracksMutex);
+				        auto it = mTracks.find(remoteMedia->mid());
+					    auto track = it != mTracks.end() ? it->second.lock() : nullptr;
+						if(track) {
+							// Prefer local description
+							auto media = track->description();
 
-						        PLOG_DEBUG << "Adding media to local description, mid=\""
-						                   << reciprocated.mid()
-						                   << "\", removed=true (track is destroyed)";
-
-						        description.addMedia(std::move(reciprocated));
-					        }
-					        return;
-				        }
-
-				        auto reciprocated = remoteMedia->reciprocate();
-#if !RTC_ENABLE_MEDIA
-				        if (!reciprocated.isRemoved()) {
-					        // No media support, mark as removed
-					        PLOG_WARNING << "Rejecting track (not compiled with media support)";
-					        reciprocated.markRemoved();
-				        }
-#endif
+						    PLOG_DEBUG << "Adding media to local description, mid=\""
+						                << media.mid() << "\", removed=" << std::boolalpha
+						                << media.isRemoved();
 
-				        PLOG_DEBUG << "Reciprocating media in local description, mid=\""
-				                   << reciprocated.mid() << "\", removed=" << std::boolalpha
-				                   << reciprocated.isRemoved();
+						    description.addMedia(std::move(media));
 
-				        // Create incoming track
-				        auto track =
-				            std::make_shared<Track>(weak_from_this(), std::move(reciprocated));
-				        mTracks.emplace(std::make_pair(track->mid(), track));
-				        mTrackLines.emplace_back(track);
-				        triggerTrack(track); // The user may modify the track description
+					    } else {
+							auto reciprocated = remoteMedia->reciprocate();
+							reciprocated.markRemoved();
 
-				        auto handler = getMediaHandler();
-				        if (handler)
-					        handler->media(track->description());
+						    PLOG_DEBUG << "Adding media to local description, mid=\""
+						                << reciprocated.mid()
+						                << "\", removed=true (track is destroyed)";
 
-				        if (track->description().isRemoved())
-					        track->close();
-
-				        description.addMedia(track->description());
+						    description.addMedia(std::move(reciprocated));
+					    }
 			        },
 			    },
 			    remote->media(i));
-
-		// We need to update the SSRC cache for newly-created incoming tracks
-		updateTrackSsrcCache(*remote);
+		}
 	}
 
 	if (description.type() == Description::Type::Offer) {
@@ -1061,22 +1029,18 @@ void PeerConnection::processLocalDescription(Description description) {
 				description.addMedia(std::move(app));
 			}
 		}
-
-		// There might be no media at this point, for instance if the user deleted tracks
-		if (description.mediaCount() == 0)
-			throw std::runtime_error("No DataChannel or Track to negotiate");
 	}
 
 	// Set local fingerprint (wait for certificate if necessary)
 	description.setFingerprint(mCertificate.get()->fingerprint());
+}
 
+void PeerConnection::processLocalDescription(Description description) {
 	PLOG_VERBOSE << "Issuing local description: " << description;
 
 	if (description.mediaCount() == 0)
 		throw std::logic_error("Local description has no media line");
 
-	updateTrackSsrcCache(description);
-
 	{
 		// Set as local description
 		std::lock_guard lock(mLocalDescriptionMutex);
@@ -1093,11 +1057,6 @@ void PeerConnection::processLocalDescription(Description description) {
 
 	mProcessor.enqueue(&PeerConnection::trigger<Description>, shared_from_this(),
 	                   &localDescriptionCallback, std::move(description));
-
-	// Reciprocated tracks might need to be open
-	if (auto dtlsTransport = std::atomic_load(&mDtlsTransport);
-	    dtlsTransport && dtlsTransport->state() == Transport::State::Connected)
-		mProcessor.enqueue(&PeerConnection::openTracks, shared_from_this());
 }
 
 void PeerConnection::processLocalCandidate(Candidate candidate) {
@@ -1121,6 +1080,40 @@ void PeerConnection::processLocalCandidate(Candidate candidate) {
 }
 
 void PeerConnection::processRemoteDescription(Description description) {
+	// Create tracks from remote description
+	for (int i = 0; i < description.mediaCount(); ++i) {
+		if (std::holds_alternative<Description::Media *>(description.media(i))) {
+			auto remoteMedia = std::get<Description::Media *>(description.media(i));
+			std::unique_lock lock(mTracksMutex); // we may emplace a track
+			if (auto it = mTracks.find(remoteMedia->mid()); it != mTracks.end())
+				continue;
+
+			PLOG_DEBUG << "New remote track, mid=\"" << remoteMedia->mid() << "\"";
+
+			auto reciprocated = remoteMedia->reciprocate();
+#if !RTC_ENABLE_MEDIA
+			if (!reciprocated.isRemoved()) {
+				// No media support, mark as removed
+				PLOG_WARNING << "Rejecting track (not compiled with media support)";
+				reciprocated.markRemoved();
+			}
+#endif
+
+			// Create incoming track
+			auto track = std::make_shared<Track>(weak_from_this(), std::move(reciprocated));
+			mTracks.emplace(std::make_pair(track->mid(), track));
+			mTrackLines.emplace_back(track);
+			triggerTrack(track); // The user may modify the track description
+
+			auto handler = getMediaHandler();
+			if (handler)
+				handler->media(track->description());
+
+			if (track->description().isRemoved())
+				track->close();
+		}
+	}
+
 	// Update the SSRC cache for existing tracks
 	updateTrackSsrcCache(description);
 
@@ -1146,9 +1139,9 @@ void PeerConnection::processRemoteDescription(Description description) {
 		mProcessor.enqueue(&PeerConnection::remoteCloseDataChannels, shared_from_this());
 	}
 
+	// Reciprocated tracks might need to be open
 	if (dtlsTransport && dtlsTransport->state() == Transport::State::Connected)
 		mProcessor.enqueue(&PeerConnection::openTracks, shared_from_this());
-
 }
 
 void PeerConnection::processRemoteCandidate(Candidate candidate) {
@@ -1238,7 +1231,7 @@ void PeerConnection::setMediaHandler(shared_ptr<MediaHandler> handler) {
 	mMediaHandler = handler;
 }
 
-shared_ptr<MediaHandler> PeerConnection::getMediaHandler() {
+shared_ptr<MediaHandler> PeerConnection::getMediaHandler() const {
 	std::shared_lock lock(mMediaHandlerMutex);
 	return mMediaHandler;
 }

+ 2 - 1
src/impl/peerconnection.hpp

@@ -75,6 +75,7 @@ struct PeerConnection : std::enable_shared_from_this<PeerConnection> {
 	void closeTracks();
 
 	void validateRemoteDescription(const Description &description);
+	void populateLocalDescription(Description &description) const;
 	void processLocalDescription(Description description);
 	void processLocalCandidate(Candidate candidate);
 	void processRemoteDescription(Description description);
@@ -84,7 +85,7 @@ struct PeerConnection : std::enable_shared_from_this<PeerConnection> {
 	bool negotiationNeeded() const;
 
 	void setMediaHandler(shared_ptr<MediaHandler> handler);
-	shared_ptr<MediaHandler> getMediaHandler();
+	shared_ptr<MediaHandler> getMediaHandler() const;
 
 	void triggerDataChannel(weak_ptr<DataChannel> weakDataChannel);
 	void triggerTrack(weak_ptr<Track> weakTrack);

+ 31 - 5
src/peerconnection.cpp

@@ -85,6 +85,7 @@ void PeerConnection::setLocalDescription(Description::Type type, LocalDescriptio
 	PLOG_VERBOSE << "Setting local description, type=" << Description::typeToString(type);
 
 	SignalingState signalingState = impl()->signalingState.load();
+
 	if (type == Description::Type::Rollback) {
 		if (signalingState == SignalingState::HaveLocalOffer ||
 		    signalingState == SignalingState::HaveLocalPranswer) {
@@ -139,11 +140,17 @@ void PeerConnection::setLocalDescription(Description::Type type, LocalDescriptio
 		return; // closed
 
 	if (init.iceUfrag && init.icePwd) {
-		PLOG_DEBUG << "Using custom ICE attributes, ufrag=\"" << init.iceUfrag.value() << "\", pwd=\"" << init.icePwd.value() << "\"";
-		iceTransport->setIceAttributes(init.iceUfrag.value(), init.icePwd.value());
+		PLOG_DEBUG << "Setting custom ICE attributes, ufrag=\"" << *init.iceUfrag << "\", pwd=\"" << *init.icePwd << "\"";
+		iceTransport->setIceAttributes(*init.iceUfrag, *init.icePwd);
 	}
 
 	Description local = iceTransport->getLocalDescription(type);
+	impl()->populateLocalDescription(local);
+
+	// There might be no media at this point, for instance if the user deleted tracks
+	if (local.mediaCount() == 0)
+		throw std::runtime_error("No DataChannel or Track to negotiate");
+
 	impl()->processLocalDescription(std::move(local));
 
 	impl()->changeSignalingState(newSignalingState);
@@ -162,9 +169,8 @@ void PeerConnection::setLocalDescription(Description::Type type, LocalDescriptio
 
 void PeerConnection::gatherLocalCandidates(std::vector<IceServer> additionalIceServers) {
 	auto iceTransport = impl()->getIceTransport();
-	if (!iceTransport) {
-		throw std::logic_error("No IceTransport. Local Description has not been set");
-	}
+	if (!iceTransport || !localDescription())
+		throw std::logic_error("Local description has not been set before gathering");
 
 	if (impl()->gatheringState == GatheringState::New) {
 		iceTransport->gatherLocalCandidates(impl()->localBundleMid(), additionalIceServers);
@@ -282,6 +288,26 @@ void PeerConnection::addRemoteCandidate(Candidate candidate) {
 	impl()->processRemoteCandidate(std::move(candidate));
 }
 
+Description PeerConnection::createOffer() {
+	auto iceTransport = impl()->initIceTransport();
+	if (!iceTransport)
+		throw std::runtime_error("Peer connection is closed");
+
+	Description desc = iceTransport->getLocalDescription(rtc::Description::Type::Offer);
+	impl()->populateLocalDescription(desc);
+	return desc;
+}
+
+Description PeerConnection::createAnswer() {
+	auto iceTransport = impl()->initIceTransport();
+	if (!iceTransport)
+		throw std::runtime_error("Peer connection is closed");
+
+	Description desc = iceTransport->getLocalDescription(rtc::Description::Type::Answer);
+	impl()->populateLocalDescription(desc);
+	return desc;
+}
+
 void PeerConnection::setMediaHandler(shared_ptr<MediaHandler> handler) {
 	impl()->setMediaHandler(std::move(handler));
 };