Browse Source

Implemented SDP bundle negociation

Paul-Louis Ageneau 5 years ago
parent
commit
fef0fb7140
3 changed files with 37 additions and 31 deletions
  1. 4 4
      include/rtc/description.hpp
  2. 32 26
      src/description.cpp
  3. 1 1
      src/icetransport.cpp

+ 4 - 4
include/rtc/description.hpp

@@ -46,7 +46,7 @@ public:
 	std::optional<string> fingerprint() const;
 	std::optional<uint16_t> sctpPort() const;
 	std::optional<size_t> maxMessageSize() const;
-	bool trickleEnabled() const;
+	bool ended() const;
 
 	void hintType(Type type);
 	void setFingerprint(string fingerprint);
@@ -80,17 +80,17 @@ private:
 
 	// Media (non-data)
 	struct Media {
+		Media(const string &mline);
+		string type;
 		string description;
 		string mid;
 		std::vector<string> attributes;
-
-		string type() const;
 	};
 	std::map<string, Media> mMedia; // by mid
 
 	// Candidates
 	std::vector<Candidate> mCandidates;
-	bool mTrickle;
+	bool mEnded = false;
 
 	static Type stringToType(const string &typeString);
 	static string typeToString(Type type);

+ 32 - 26
src/description.cpp

@@ -50,7 +50,7 @@ Description::Description(const string &sdp, const string &typeString)
 Description::Description(const string &sdp, Type type) : Description(sdp, type, Role::ActPass) {}
 
 Description::Description(const string &sdp, Type type, Role role)
-    : mType(Type::Unspec), mRole(role), mIceUfrag(""), mIcePwd(""), mTrickle(true) {
+    : mType(Type::Unspec), mRole(role) {
 	mData.mid = "data";
 	hintType(type);
 
@@ -68,15 +68,11 @@ Description::Description(const string &sdp, Type type, Role role)
 		finished = !std::getline(ss, line) && line.empty();
 		trim_end(line);
 
-		if (!finished) {
-			LOG_VERBOSE << "SDP line: " << line;
-		}
-
 		// Media description line (aka m-line)
 		if (finished || match_prefix(line, "m=")) {
 			if (currentMedia) {
 				if (!currentMedia->mid.empty()) {
-					if (currentMedia->type() == "application")
+					if (currentMedia->type == "application")
 						mData.mid = currentMedia->mid;
 					else
 						mMedia.emplace(currentMedia->mid, std::move(*currentMedia));
@@ -86,7 +82,7 @@ Description::Description(const string &sdp, Type type, Role role)
 				}
 			}
 			if (!finished)
-				currentMedia.emplace(Media{line.substr(2)});
+				currentMedia.emplace(Media(line.substr(2)));
 
 			// Attribute line
 		} else if (match_prefix(line, "a=")) {
@@ -131,7 +127,7 @@ Description::Description(const string &sdp, Type type, Role role)
 			} else if (key == "candidate") {
 				addCandidate(Candidate(attr, currentMedia ? currentMedia->mid : mData.mid));
 			} else if (key == "end-of-candidates") {
-				mTrickle = false;
+				mEnded = true;
 			} else if (currentMedia) {
 				currentMedia->attributes.emplace_back(line.substr(2));
 			}
@@ -155,8 +151,7 @@ std::optional<uint16_t> Description::sctpPort() const { return mData.sctpPort; }
 
 std::optional<size_t> Description::maxMessageSize() const { return mData.maxMessageSize; }
 
-bool Description::trickleEnabled() const { return mTrickle; }
-
+bool Description::ended() const { return mEnded; }
 
 void Description::hintType(Type type) {
 	if (mType == Type::Unspec) {
@@ -178,12 +173,12 @@ void Description::addCandidate(Candidate candidate) {
 	mCandidates.emplace_back(std::move(candidate));
 }
 
-void Description::endCandidates() { mTrickle = false; }
+void Description::endCandidates() { mEnded = true; }
 
 std::vector<Candidate> Description::extractCandidates() {
 	std::vector<Candidate> result;
 	std::swap(mCandidates, result);
-	mTrickle = true;
+	mEnded = false;
 	return result;
 }
 
@@ -212,11 +207,25 @@ string Description::generateSdp(const string &eol) const {
 	sdp << "t=0 0" << eol;
 
 	// Bundle
+	// see Negotiating Media Multiplexing Using the Session Description Protocol
+	// https://tools.ietf.org/html/draft-ietf-mmusic-sdp-bundle-negotiation-54
 	sdp << "a=group:BUNDLE";
 	for (const auto &[mid, _] : mMedia)
 		sdp << " " << mid;
 	sdp << " " << mData.mid << eol;
 
+	// Data
+	const string dataDescription = "UDP/DTLS/SCTP webrtc-datachannel";
+	sdp << "m=application" << ' ' << (!mMedia.empty() ? 0 : 9) << ' ' << dataDescription << eol;
+	sdp << "c=IN IP4 0.0.0.0" << eol;
+	if (!mMedia.empty())
+		sdp << "a=bundle-only" << eol;
+	sdp << "a=mid:" << mData.mid << eol;
+	if (mData.sctpPort)
+		sdp << "a=sctp-port:" << *mData.sctpPort << eol;
+	if (mData.maxMessageSize)
+		sdp << "a=max-message-size:" << *mData.maxMessageSize << eol;
+
 	// Non-data media
 	if (!mMedia.empty()) {
 		// Lip-sync
@@ -227,26 +236,17 @@ string Description::generateSdp(const string &eol) const {
 
 		// Descriptions and attributes
 		for (const auto &[_, media] : mMedia) {
-			sdp << "m=" << media.description << eol;
+			sdp << "m=" << media.type << ' ' << 0 << ' ' << media.description << eol;
 			sdp << "c=IN IP4 0.0.0.0" << eol;
+			sdp << "a=bundle-only" << eol;
 			sdp << "a=mid:" << media.mid << eol;
 			for (const auto &attr : media.attributes)
 				sdp << "a=" << attr << eol;
 		}
 	}
 
-	// Data
-	sdp << "m=application 9 UDP/DTLS/SCTP webrtc-datachannel" << eol;
-	sdp << "c=IN IP4 0.0.0.0" << eol;
-	sdp << "a=mid:" << mData.mid << eol;
-	if (mData.sctpPort)
-		sdp << "a=sctp-port:" << *mData.sctpPort << eol;
-	if (mData.maxMessageSize)
-		sdp << "a=max-message-size:" << *mData.maxMessageSize << eol;
-
 	// Common
-	if (mTrickle)
-		sdp << "a=ice-options:trickle" << eol;
+	sdp << "a=ice-options:trickle" << eol;
 	sdp << "a=ice-ufrag:" << mIceUfrag << eol;
 	sdp << "a=ice-pwd:" << mIcePwd << eol;
 	sdp << "a=setup:" << roleToString(mRole) << eol;
@@ -257,13 +257,19 @@ string Description::generateSdp(const string &eol) const {
 	// Candidates
 	for (const auto &candidate : mCandidates)
 		sdp << string(candidate) << eol;
-	if (!mTrickle)
+	if (mEnded)
 		sdp << "a=end-of-candidates" << eol;
 
 	return sdp.str();
 }
 
-string Description::Media::type() const { return description.substr(0, description.find(' ')); }
+Description::Media::Media(const string &mline) {
+	size_t p = mline.find(' ');
+	this->type = mline.substr(0, p);
+	if (p != string::npos)
+		if (size_t q = mline.find(' ', p + 1); q != string::npos)
+			this->description = mline.substr(q + 1);
+}
 
 Description::Type Description::stringToType(const string &typeString) {
 	if (typeString == "offer")

+ 1 - 1
src/icetransport.cpp

@@ -481,7 +481,7 @@ void IceTransport::setRemoteDescription(const Description &description) {
 	mRole = description.role() == Description::Role::Active ? Description::Role::Passive
 	                                                        : Description::Role::Active;
 	mMid = description.dataMid();
-	mTrickleTimeout = description.trickleEnabled() ? 30s : 0s;
+	mTrickleTimeout = !description.ended() ? 30s : 0s;
 
 	// Warning: libnice expects "\n" as end of line
 	if (nice_agent_parse_remote_sdp(mNiceAgent.get(), description.generateSdp("\n").c_str()) < 0)